Использование HAVING без GROUP BY не работает должным образом - PullRequest
0 голосов
/ 10 ноября 2018

Я начинаю изучать 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

Ошибка:

enter image description here

Ответы [ 3 ]

0 голосов
/ 10 ноября 2018

В 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
0 голосов
/ 10 ноября 2018

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
0 голосов
/ 10 ноября 2018

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

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 

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

Да и нет. Наличие фильтра на ваших агрегированных данных. Выберите говорит, какие столбцы / информацию вернуть. Таким образом, вы должны спросить "как бы выглядел результат?" то есть, учитывая, что нам пришлось эффективно применять 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.

...