HAVING

HAVING фильтрует группы после GROUP BY и вычисления агрегатов.

SELECT user_id,
       COUNT(*) AS order_count,
       SUM(total) AS amount
FROM orders
WHERE state = 'PAID'
GROUP BY user_id
HAVING SUM(total) >= 10000;

WHERE оставил только оплаченные заказы, HAVING — пользователей с достаточной общей суммой.

Не переносить обычный фильтр без причины

-- Лучше фильтровать строки раньше:
WHERE state = 'PAID'
 
-- А не полагаться на поздний фильтр:
HAVING state = 'PAID'

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

Без GROUP BY

Запрос без GROUP BY рассматривает весь вход как одну группу:

SELECT COUNT(*) AS order_count
FROM orders
HAVING COUNT(*) > 0;

Он вернет одну строку, если есть заказы, иначе ни одной. Для простой проверки существования обычно понятнее EXISTS.

Ловушки

  • HAVING COUNT(column) = 0 и HAVING COUNT(*) = 0 различаются при NULL и outer join.
  • Псевдонимы из SELECT в HAVING поддерживаются не во всех диалектах.
  • HAVING не заменяет WHERE: поздняя фильтрация может увеличить объем группировки.

Связано: Оператор GROUP BY, Агрегатные функции, Условный оператор WHERE.

Вопросы

Главное отличие WHERE от HAVING? Первый фильтрует строки, второй — группы.

Можно ли использовать агрегат в WHERE? Не на том же уровне запроса; агрегация логически происходит позже.

Обязателен ли GROUP BY для HAVING? Нет, весь набор может быть одной группой.