WHERE и HAVING путают очень часто, потому что обе конструкции что-то фильтруют. Но фильтруют они на разных этапах выполнения запроса.

И это ключевая идея:

  • WHERE фильтрует строки до группировки
  • HAVING фильтрует результат после группировки

Если это понять, путаница почти исчезает.

Простая логика

Представь запрос как конвейер:

  1. Берем строки из таблицы
  2. Фильтруем часть строк
  3. Группируем
  4. Считаем агрегаты
  5. Фильтруем группы

Вот где работают эти конструкции:

  • WHERE -> шаг 2
  • HAVING -> шаг 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(*) > 5
  • SUM(revenue) > 100000
  • AVG(check_amount) > 1500
  • MAX(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-запроса.