Почему нельзя использовать агрегатные функции в WHERE и как использовать HAVING в SQL?
Что проверяют
Знаете ли вы логический порядок выполнения запроса. Ответ «WHERE до группировки, HAVING после» показывает, что вы понимаете, откуда берётся ограничение, а не заучили правило.
Как отвечать
Логический порядок такой: FROM и соединения, затем WHERE, затем GROUP BY, затем HAVING, затем SELECT, затем ORDER BY и LIMIT.
WHERE работает на этапе, когда групп ещё нет, есть отдельные строки. Агрегат считается по группе, значит на этом этапе для него нет входных данных, и запрос отвергается при разборе. HAVING выполняется после группировки и фильтрует уже готовые группы.
select department_id, count(*) as cnt, avg(salary) as avg_salary
from employees
where hired_at >= :since
group by department_id
having count(*) > 10
order by cnt desc;Здесь where отсеивает строки до группировки, а having отсеивает готовые группы.
Практическое следствие: всё, что фильтруется по одной строке, должно жить в WHERE, потому что это уменьшает объём данных до группировки и позволяет использовать индекс. Перенос такого условия в HAVING даст тот же результат, но заставит сгруппировать лишнее.
HAVING без GROUP BY тоже работает: вся выборка считается одной группой, и такой запрос вернёт либо одну строку, либо ничего.
Где спотыкаются
Ссылаются в WHERE на псевдоним из SELECT. Он ещё не вычислен, и запрет тут той же природы, что и с агрегатами. В HAVING поведение зависит от СУБД: часть движков псевдонимы принимает, часть требует повторить выражение целиком.
Второе: пишут оконную функцию в WHERE или HAVING. Окна считаются позже, поэтому фильтровать по номеру строки можно только снаружи, через подзапрос или CTE.
Третье: путают count(*) и count(col). Второй не считает NULL, поэтому условие в HAVING тихо отсекает группы, где нужный столбец пуст, и цифры в отчёте расходятся с ожидаемыми.