Что означает HAVING без GROUP BY в агрегатном запросе?
В агрегатном запросе HAVING без GROUP BY проверяет единственную неявную группу, содержащую все строки результата после применения WHERE. Если условие истинно, возвращается одна агрегированная строка; если ложно — не возвращается ни одной строки.
Например, условие по SUM в HAVING отфильтрует уже рассчитанный общий итог, а не отдельные исходные строки.
Агрегация появилась как способ получать сводные показатели по множеству строк: сумму, количество, минимум или максимум. GROUP BY разделяет исходный набор на несколько групп, но для отчёта по всему набору отдельное разбиение не требуется.
Поэтому агрегатный запрос без GROUP BY рассматривает весь набор строк как одну группу. HAVING позволяет проверить результат этой единственной агрегации.
Представим, что нужно вернуть общий оборот только в том случае, если он превышает заданный порог. Фильтрация через WHERE была бы ошибочной: WHERE отбрасывает исходные строки до вычисления суммы и тем самым меняет сам агрегат.
Если использовать HAVING, сначала вычисляется общий оборот, а затем проверяется условие. При невыполнении условия запрос обычно возвращает пустой результат, а не строку с нулевым значением.
Логически обработка выглядит так: FROM формирует исходный набор, WHERE фильтрует строки, агрегатные функции вычисляют значения единственной группы, затем HAVING проверяет условие этой группы.
В этом примере сумма равна 300, поэтому результат содержит одну строку: количество строк — 3, сумма — 300. Если заменить условие на SUM(amount) > 400, результатом будет пустой набор.
Важна разница между WHERE и HAVING. WHERE подходит для условий на отдельных исходных строках, например для исключения отменённых заказов до суммирования. HAVING подходит для условий на вычисленном агрегате, например для проверки общего оборота.
У агрегатного запроса без GROUP BY есть особенность пустого входного набора: обычно он всё равно формирует одну неявную группу. При этом COUNT возвращает 0, а большинство других агрегатов, включая SUM, возвращают NULL. HAVING может удалить и эту строку, если сравнение с NULL не даст TRUE.
Переносимость требует осторожности: HAVING должен ссылаться на агрегаты или допустимые выражения группировки. Попытка использовать в нём обычный столбец, не входящий в GROUP BY и не агрегированный, либо является ошибкой, либо зависит от нестандартного поведения конкретной СУБД.
В ежедневном отчёте нужно отправлять уведомление только тогда, когда суммарная стоимость всех успешно оплаченных заказов за день превышает лимит.
Первый вариант — применить условие по стоимости через WHERE. Это неверно, если условие относится к общей сумме: WHERE оставит только отдельные подходящие строки, после чего сумма будет рассчитана по изменённому набору.
Второй вариант — сначала вычислить сумму во вложенном запросе, а затем отфильтровать результат внешним WHERE. Такой подход корректен, но требует дополнительного уровня запроса.
Выбранный вариант — агрегатный запрос с WHERE для статуса заказа и HAVING для общего оборота. Он сохраняет правильный порядок вычислений, возвращает одну строку при выполнении порога и пустой результат при его невыполнении.
Нет, не напрямую. WHERE выполняется до агрегации и не может проверять результат SUM для всей группы. Он может фильтровать только исходные строки; для условия по SUM нужен HAVING или внешний запрос над уже агрегированным результатом.
Логически формируется одна неявная группа, хотя агрегаты получают пустой набор строк. Поэтому COUNT обычно равен 0, а SUM, AVG, MIN и MAX обычно равны NULL. Если условие HAVING сравнивает такой агрегат с числом, результат сравнения не является TRUE, и строка отбрасывается.
В стандартно проверяемом и переносимом SQL такой запрос следует считать некорректным: для одной группы нет однозначного значения этого столбца. Нужно либо добавить столбец в GROUP BY, либо применить к нему агрегат, либо перенести фильтр исходных строк в WHERE. Поведение, разрешённое отдельной СУБД, не следует считать переносимым решением.