WHERE vs HAVING в SQL — в чём разница и когда что использовать
Коротко
WHERE фильтрует строки до группировки. HAVING фильтрует группы после GROUP BY. Это ключевое отличие, которое спрашивают на каждом втором собеседовании аналитика.
Что делает WHERE
WHERE отсеивает строки из исходной таблицы до того, как происходит группировка и агрегация. Он работает с отдельными строками и не может использовать агрегатные функции (COUNT, SUM, AVG).
SELECT department, COUNT(*) AS cnt
FROM employees
WHERE salary > 100000
GROUP BY department;Здесь WHERE сначала убирает всех с зарплатой ≤ 100 000, а потом считает оставшихся по отделам.
Что делает HAVING
HAVING фильтрует уже сгруппированные данные. Он работает после GROUP BY и может использовать агрегатные функции.
SELECT department, COUNT(*) AS cnt
FROM employees
GROUP BY department
HAVING COUNT(*) > 5;Здесь SQL сначала группирует всех сотрудников по отделам, считает количество, и только потом оставляет отделы с более чем 5 сотрудниками.
Ключевые отличия
| WHERE | HAVING | |
|---|---|---|
| Когда выполняется | До GROUP BY | После GROUP BY |
| Работает с | Отдельными строками | Группами строк |
| Агрегатные функции | Нельзя | Можно |
| Без GROUP BY | Работает | Работает — вся таблица как одна группа |
| Производительность | Быстрее — отсеивает строки раньше | Медленнее — работает после группировки |
Порядок выполнения SQL-запроса
Чтобы понять разницу, нужно знать порядок выполнения:
- FROM — выбрать таблицу
- WHERE — отфильтровать строки
- GROUP BY — сгруппировать
- HAVING — отфильтровать группы
- SELECT — выбрать колонки
- ORDER BY — отсортировать
- LIMIT — ограничить результат
WHERE стоит на шаге 2, HAVING — на шаге 4. Поэтому WHERE не видит результатов агрегации, а HAVING — видит.
Когда использовать вместе
WHERE и HAVING можно и нужно комбинировать. WHERE сначала убирает ненужные строки, а HAVING потом фильтрует группы:
SELECT category, AVG(price) AS avg_price
FROM products
WHERE is_active = TRUE
GROUP BY category
HAVING AVG(price) > 1000;Сначала отбрасываем неактивные товары (WHERE), группируем по категориям, потом оставляем только категории со средней ценой выше 1 000 (HAVING).
Совет для собеседования: всегда старайтесь фильтровать через WHERE, а не через HAVING. WHERE работает до группировки и обрабатывает меньше данных — это эффективнее.
Типичная ошибка
Частая ошибка — использовать HAVING вместо WHERE для фильтрации обычных строк:
-- Неправильно (работает, но медленно)
SELECT department, COUNT(*)
FROM employees
GROUP BY department
HAVING department = 'Sales';
-- Правильно
SELECT department, COUNT(*)
FROM employees
WHERE department = 'Sales'
GROUP BY department;Оба запроса вернут одинаковый результат, но второй эффективнее: он сразу отбирает нужный отдел и группирует только его.
Вопросы с собеседований
- «Можно ли использовать WHERE после GROUP BY?» — Нет, синтаксически WHERE всегда идёт до GROUP BY.
- «Можно ли в HAVING обращаться к алиасу из SELECT?» — Зависит от СУБД. PostgreSQL позволяет, стандартный SQL — нет.
- «Что быстрее — WHERE или HAVING?» — WHERE, потому что фильтрует раньше и обрабатывает меньше данных.
FAQ
Можно ли использовать HAVING без GROUP BY?
Формально да — SQL выполнит запрос. Но смысла в этом мало: без группировки HAVING работает как WHERE для агрегации по всей таблице.
Почему нельзя писать WHERE COUNT(*) > 5?
Потому что WHERE выполняется до группировки. На этом этапе SQL ещё не знает значений агрегатных функций — их просто не из чего считать.
Что если условие можно написать и в WHERE, и в HAVING?
Всегда пишите в WHERE. Это быстрее, потому что строки отсеиваются до группировки и агрегации.