Использование HAVING без GROUP BY не работает должным образом

Я начинаю изучать SQL Server, в документации, найденной в состояниях MSDN, как это

HAVING обычно используется с предложением GROUP BY. Когда GROUP BY не используется, существует неявная единая агрегированная группа.

Это заставило меня думать, что мы можем использовать наличие без предложения groupBy, но когда я пытаюсь сделать запрос, я не могу его использовать.

У меня есть такой стол

CREATE TABLE [dbo].[_abc]
(
    [wage] [int] NULL
) ON [PRIMARY]
GO

INSERT INTO [dbo].[_abc] (wage)
VALUES (4), (8), (15), (30), (50) 
GO

Теперь, когда я запускаю этот запрос, я получаю сообщение об ошибке

select * 
from [dbo].[_abc]
having sum(wage) > 5

Ошибка:

введите описание изображения здесь

3 ответа

Решение

Документация верна; то есть вы можете запустить это утверждение:

select sum(wage) sum_of_all_wages
, count(1) count_of_all_records
from [dbo].[_abc] 
having sum(wage) > 5

Причина, по которой ваше утверждение не работает, заключается в select *, что означает выбор значения каждого столбца. Когда нет group byвсе записи агрегированы; т.е. вы получаете только 1 запись в вашем наборе результатов, которая должна представлять каждую запись. Таким образом, вы можете * только включать значения, предоставленные агрегатными функциями к вашим столбцам; не сами столбцы. * конечно, вы также можете указать константы, поэтому select 'x' constant, count(1) cnt from myTable должно сработать.

Существует не так много вариантов использования, которые я мог бы придумать, где бы вы хотели использовать без группы, но, безусловно, это можно сделать, как показано выше.

NB. Если вы хотите, чтобы во всех строках зарплата была больше 5, вы бы использовали where пункт вместо:

select * 
from [dbo].[_abc] 
where wage > 5

Точно так же, если вы хотите, чтобы сумма всех зарплат была больше 5, вы можете сделать это

select sum(wage) sum_of_wage_over_5 
from [dbo].[_abc] 
where wage > 5

Или, если вы хотите сравнить сумму заработной платы свыше 5 с теми, которые указаны ниже:

select case when wage > 5 then 1 else 0 end wage_over_five
, sum(wage) sum_of_wage
from [dbo].[_abc] 
group by case when wage > 5 then 1 else 0 end 

Смотрите выполнимые примеры здесь.


Обновление на основе комментариев:

Тебе нужно having использовать агрегатные функции?

Нет, ты можешь бежать select sum(wage) from [dbo].[_abc], Когда агрегатная функция используется без group by предложение, это как если бы вы группировали по константе; т.е. select sum(wage) from [dbo].[_abc] group by 1,

Документация просто означает, что в то время как обычно у вас есть having заявление с group by Заявление, это нормально, чтобы исключить group by / в таких случаях having утверждение, как select оператор, обработает ваш запрос, как если бы вы указали group by 1

В чем смысл?

Трудно придумать много хороших вариантов использования, так как вы получаете только один ряд назад и having заявление является фильтром на этом.

Одним из вариантов использования может быть то, что вы пишете код для мониторинга ваших лицензий на некоторое программное обеспечение; если у вас меньше пользователей, чем на пользователя, все хорошо / вы не хотите видеть результат, потому что вам все равно. Если у вас есть больше пользователей, вы хотите знать об этом. Например

declare @totalUserLicenses int = 100
select count(1) NumberOfActiveUsers
, @totalUserLicenses NumberOfLicenses
, count(1) - @totalUserLicenses NumberOfAdditionalLicensesToPurchase
from [dbo].[Users]
where enabled = 1
having count(1) > @totalUserLicenses 

Разве выбор не имеет отношения к положению, имеющему?

И да и нет. Наличие фильтра на ваших агрегированных данных. Выберите говорит, какие столбцы / информацию, чтобы вернуть. Таким образом, вы должны спросить "как бы выглядел результат?" т.е. учитывая, что мы должны были эффективно применять group by 1 использовать having Скажите, как интерпретировать SQL select *? Поскольку в вашей таблице только один столбец, это будет означать select wage; но у нас есть 5 строк, поэтому 5 разных значений wageи только 1 строка в результате, чтобы показать это.

Я думаю, вы могли бы сказать: "Я хочу вернуть все строки, если их сумма больше 5; в противном случае я не хочу возвращать какие-либо строки". Если бы ваше требование было достигнуто различными способами; один из которых будет:

select *
from [dbo].[_abc] 
where exists 
(
    select 1 
    from [dbo].[_abc] 
    having sum(wage) > 5
) 

Однако мы должны написать код, отвечающий требованиям, а не ожидать, что код поймет наши намерения.

Еще один способ думать о having как быть where заявление применяется к подзапросу. Т.е. ваше оригинальное утверждение эффективно гласит:

select wage
from
(
    select sum(wage) sum_of_wage
    from [dbo].[_abc]
    group by 1
) singleRowResult
where sum_of_wage > 5

Это не будет работать, потому что wage недоступен для внешнего запроса; только sum_of_wage возвращается

HAVING без GROUP BY Предложение совершенно верно, но вот что вам нужно понять:

  • Результат будет содержать ноль или одну строку
    • Неявный GROUP BY вернет ровно одну строку, даже если WHERE состояние соответствует нулю строк
    • HAVING сохранит или исключит эту единственную строку в зависимости от условия
  • Любой столбец в SELECT предложение должно быть заключено в агрегатную функцию
  • Вы также можете указать выражение, если оно не зависит от столбцов.

Что означает, что вы можете сделать это:

SELECT SUM(wage)
FROM employees
HAVING SUM(wage) > 100
-- One row containing the sum if the sum is greater than 5
-- Zero rows otherwise

Или даже это:

SELECT 1
FROM employees
HAVING SUM(wage) > 100
-- One row containing "1" if the sum is greater than 5
-- Zero rows otherwise

Эта конструкция часто используется, когда вам интересно проверить, найдено ли совпадение для агрегата:

SELECT *
FROM departments
WHERE EXISTS (
    SELECT 1
    FROM employees
    WHERE employees.department = departments.department
    HAVING SUM(wage) > 100
)
-- all departments whose employees earn more than 100 in total

В SQL вы не можете возвращать агрегированные функционирующие столбцы напрямую. Вам нужно сгруппировать неагрегированные поля

Как показано ниже пример

 USE AdventureWorks2012 ;  
GO  
SELECT SalesOrderID, SUM(LineTotal) AS SubTotal  
FROM Sales.SalesOrderDetail  
GROUP BY SalesOrderID  
HAVING SUM(LineTotal) > 100000.00  
ORDER BY SalesOrderID ;  

В вашем случае у вас нет столбца идентификаторов для вашей таблицы, он должен выглядеть следующим образом

Alter _abc
Add Id_new Int Identity(1, 1)
Go
Другие вопросы по тегам