Использование 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