Условие
В чём разница между WHERE и HAVING в SELECT-запросе?
Решение
| WHERE | HAVING | |
|---|---|---|
| Когда выполняется | До GROUP BY |
После GROUP BY |
| Над чем фильтрует | Отдельные строки | Группы (агрегатные значения) |
| Можно ли использовать агрегаты | Нет | Да |
| Производительность | Быстрее (меньше строк в агрегации) | Медленнее (агрегация всех строк) |
Пример
-- неправильно: AVG в WHERE
SELECT department, AVG(salary)
FROM employees
WHERE AVG(salary) > 5000 -- ошибка
GROUP BY department;
-- правильно
SELECT department, AVG(salary)
FROM employees
GROUP BY department
HAVING AVG(salary) > 5000;
-- ещё правильнее: фильтр строк до агрегации в WHERE
SELECT department, AVG(salary)
FROM employees
WHERE hired_at >= '2020-01-01' -- фильтрация до GROUP BY
GROUP BY department
HAVING AVG(salary) > 5000; -- фильтрация групп послеПравило выбора
- Условие на строку (без агрегата) →
WHERE. - Условие на группу (с агрегатом) →
HAVING. - Если можно положить условие в
WHERE— клади. Это уменьшит объём данных для агрегации.
Ловушка с производительностью
-- плохо: агрегируем все 100M строк, потом фильтруем
SELECT user_id, COUNT(*) FROM events
GROUP BY user_id
HAVING user_id < 1000;
-- хорошо: фильтруем сначала
SELECT user_id, COUNT(*) FROM events
WHERE user_id < 1000
GROUP BY user_id;Подводные камни
HAVINGбезGROUP BY— синтаксически валиден, работает как фильтр над всем результатом (вся выборка как одна группа).- Алиас в
HAVING— в PostgreSQL/MySQL разрешён (HAVING cnt > 5), в Oracle — только выражение целиком. COUNT(*)vsCOUNT(col)вHAVING— последний пропускаетNULL, может дать сюрприз.
Эталонный ответ
WHERE фильтрует отдельные строки до агрегации, HAVING фильтрует группы после GROUP BY. Условия без агрегата всегда кладите в WHERE — быстрее.