HAVING
Как отфильтровать уже готовые группы после GROUP BY
Теория
Представьте ситуацию.
Мы посчитали, сколько пользователей зарегистрировалось из каждого города. Но теперь нам не нужны города, где зарегистрирован всего один человек. Мы хотим оставить только те города, где пользователей хотя бы два.
Оператор WHERE в таком случае уже не поможет, потому что он работает раньше группировки и фильтрует отдельные строки, а не готовые группы.
Для таких задач нужен оператор HAVING.
Он работает после GROUP BY и проверяет уже не отдельную строку таблицы, а итог по группе.
Проще говоря, разница такая:
• WHERE — решает, какие строки попадут в расчёт;
• HAVING — решает, какие уже посчитанные группы останутся в результате.
Обычно необходимость HAVING можно увидеть по таким формулировкам:
• показать только те города, где пользователей больше 5;
• оставить только те статусы, где заказов не меньше 3;
• показать только те отделы, где средняя зарплата выше 90000.
То есть здесь нас интересует уже не просто строка таблицы, а итог по группе: количество, сумма, среднее, максимум или минимум.
Синтаксис
SELECT column_name,
aggregate_function(other_column)
FROM table_name
GROUP BY column_name
HAVING aggregate_function(other_column) > value;
SQL
И только после этого подключается HAVING.
Он смотрит на уже готовый результат группировки и решает, какие группы оставить, а какие убрать.
Поэтому внутри HAVING обычно и пишут агрегатные функции: COUNT(*), AVG(salary), SUM(quantity) и так далее.
Ведь проверяем мы именно итог по группе.
Если условие относится к отдельным строкам до группировки, ему место в WHERE. Если условие относится к уже посчитанной группе, ему место в HAVING.
Примеры
1. Города, где зарегистрировано больше пяти пользователей
🔄 Попробуйте изменить запрос:
- • Поменяйте условие на HAVING COUNT(*) >= 2
- • Уберите COALESCE и посмотрите, как будет выглядеть группа с NULL
2. Отделы, где средняя зарплата выше 90000
🔄 Попробуйте изменить запрос:
- • Поменяйте порог на 93000
- • Попробуйте вместо AVG(salary) использовать MAX(salary)
3. Статусы заказов в 2024 году, которые встречаются минимум два раза
🔄 Попробуйте изменить запрос:
- • Поменяйте условие на HAVING COUNT(*) > 6
4. Почему в HAVING нужно писать полное выражение, а не псевдоним
🔄 Попробуйте изменить запрос:
- • Попробуйте заменить COUNT(*) в HAVING на workers_count и посмотрите, что произойдёт
5. Пользователи, у которых есть минимум два заказа с указанным статусом
🔄 Попробуйте изменить запрос:
- • Поменяйте условие на HAVING COUNT(*) >= 5
- • Попробуйте заменить WHERE status IS NOT NULL на WHERE status IS NULL и сравните результат
Типичные ошибки
Путают WHERE и HAVING
WHERE работает по отдельным строкам до группировки. HAVING работает по уже готовым группам после GROUP BY. Это главное различие темы.
Пытаются писать агрегатное условие в WHERE
Запись вроде WHERE COUNT(*) > 2 не подходит. В этот момент SQL ещё не посчитал группы. Такое условие должно быть в HAVING.
Пишут в HAVING обычное условие, которое лучше подошло бы для WHERE
Например, если нужно оставить только заказы за 2024 год, это лучше сделать через WHERE order_date BETWEEN '2024-01-01' AND '2024-12-31', а не тащить это в HAVING. Так запрос и понятнее, и логичнее.
Забывают, что HAVING фильтрует уже готовые итоги
Если группа не прошла условие в HAVING, она исчезает целиком. Не одна строка из группы, а вся группа сразу.
Пытаются использовать в HAVING псевдоним из SELECT
В PostgreSQL такая запись может привести к ошибке. Поэтому в HAVING лучше сразу писать само агрегатное выражение целиком: HAVING COUNT(*) >= 2, а не псевдоним вроде orders_count.
Практика
Проверь себя
Ответьте на вопросы, чтобы закрепить материал: