WHERE и HAVING путают очень часто, потому что обе конструкции что-то фильтруют. Но фильтруют они на разных этапах выполнения запроса.
И это ключевая идея:
WHEREфильтрует строки до группировкиHAVINGфильтрует результат после группировки
Если это понять, путаница почти исчезает.
Простая логика
Представь запрос как конвейер:
- Берем строки из таблицы
- Фильтруем часть строк
- Группируем
- Считаем агрегаты
- Фильтруем группы
Вот где работают эти конструкции:
WHERE-> шаг 2HAVING-> шаг 5
Пример с WHERE
Пусть есть таблица заказов:
SELECT
customer_id,
amount,
status
FROM orders
Нужно взять только оплаченные заказы:
SELECT *
FROM orders
WHERE status = 'paid'
Здесь все логично: мы фильтруем обычные строки по обычному столбцу.
Пример с HAVING
Теперь другая задача: найти клиентов, у которых сумма оплаченных заказов больше 10000.
SELECT
customer_id,
SUM(amount) AS total_amount
FROM orders
WHERE status = 'paid'
GROUP BY customer_id
HAVING SUM(amount) > 10000
Что произошло:
WHERE status = 'paid'убрал ненужные строки до агрегацииGROUP BY customer_idсобрал строки в группыHAVING SUM(amount) > 10000оставил только нужные группы
Почему нельзя заменить HAVING на WHERE
Потому что WHERE не умеет работать с агрегатами после группировки.
Неправильно:
SELECT
customer_id,
SUM(amount) AS total_amount
FROM orders
WHERE SUM(amount) > 10000
GROUP BY customer_id
Такой запрос в большинстве СУБД просто выдаст ошибку.
Причина простая: на этапе WHERE агрегат SUM(amount) еще не посчитан.
Когда использовать WHERE
Используй WHERE, когда фильтр относится к исходным строкам:
status = 'paid'created_at >= '2026-01-01'country = 'RU'amount > 0
То есть когда тебе не нужно сначала группировать данные.
Когда использовать HAVING
Используй HAVING, когда фильтр относится к результату агрегации:
COUNT(*) > 5SUM(revenue) > 100000AVG(check_amount) > 1500MAX(event_time) >= CURRENT_DATE - INTERVAL '7 day'
Здесь сначала появляется группа, потом считается агрегат, потом уже идет отбор.
WHERE и HAVING часто используются вместе
Это вообще нормальная ситуация.
Например:
SELECT
category,
COUNT(*) AS products_cnt,
AVG(price) AS avg_price
FROM products
WHERE is_active = 1
GROUP BY category
HAVING COUNT(*) >= 10
Здесь:
WHEREубирает неактивные товарыHAVINGоставляет только те категории, где хотя бы 10 активных товаров
Именно так чаще всего и выглядит реальная аналитика.
Частая ошибка №1: писать в HAVING то, что должно быть в WHERE
Например:
SELECT
category,
COUNT(*) AS cnt
FROM products
GROUP BY category
HAVING category = 'books'
Так написать можно, но логически и по производительности чаще лучше так:
SELECT
category,
COUNT(*) AS cnt
FROM products
WHERE category = 'books'
GROUP BY category
Почему второй вариант лучше:
- фильтрация происходит раньше
- СУБД обрабатывает меньше строк
- запрос легче читать
Правило простое: если условие можно применить до группировки, чаще всего надо писать его в WHERE.
Частая ошибка №2: думать, что HAVING нужен только с GROUP BY
В большинстве практических задач HAVING действительно идет вместе с GROUP BY. Но формально некоторые СУБД позволяют использовать HAVING и без него, если запрос содержит агрегаты.
Например:
SELECT COUNT(*) AS cnt
FROM orders
HAVING COUNT(*) > 0
Но в повседневной аналитике это используется редко. Для понимания темы лучше запомнить рабочее правило:
HAVINGпочти всегда нужен для фильтрации агрегированных групп
Что быстрее: WHERE или HAVING
Если условие можно перенести в WHERE, обычно это лучше и быстрее.
Потому что:
WHEREуменьшает объем данных до группировкиHAVINGработает уже после того, как группировка и агрегаты посчитаны
Поэтому с точки зрения оптимизации:
- сначала старайся отсечь лишние строки в
WHERE - потом уже фильтруй агрегаты через
HAVING
Как быстро запомнить
Удобная запоминалка:
WHERE-> фильтр по строкамHAVING-> фильтр по группам
Или еще проще:
- до
GROUP BY->WHERE - после
GROUP BY->HAVING
Итог
Разница между HAVING и WHERE в SQL не в том, что они “разные фильтры”, а в том, на каком этапе запроса они работают:
WHEREфильтрует исходные строкиHAVINGфильтрует уже агрегированный результат
Если условие относится к обычным столбцам, почти всегда нужен WHERE.
Если условие относится к SUM, COUNT, AVG и другим агрегатам, нужен HAVING.
И это одна из тех тем, которые часто спрашивают на собеседованиях, потому что она быстро показывает, понимает ли человек реальный порядок выполнения SQL-запроса.