Условие
Таблица Employees(id, name, department, salary). Найдите отделы, у которых средняя зарплата выше 100 000, при этом исключите из расчёта стажёров с зарплатой меньше 30 000.
Объясните, в каких условиях должны быть две фильтрации.
Решение
Подход
- Условие на строки (отдельных сотрудников) —
WHERE, до агрегирования. - Условие на группу (после
GROUP BY) —HAVING.
SELECT
department,
AVG(salary) AS avg_salary,
COUNT(*) AS headcount
FROM Employees
WHERE salary >= 30000 -- отрезаем стажёров до агрегации
GROUP BY department
HAVING AVG(salary) > 100000; -- условие на агрегат группыПочему именно так
WHERE salary >= 30000исполняется на этапе сканирования таблицы и уменьшает входной объём дляGROUP BY. Если поставить это вHAVING, СУБД сначала посчитает среднее, потом начнёт фильтровать — потеря времени и неправильный смысл (нельзя сказатьHAVING salary >= 30000, потому что в группе уже нет отдельнойsalary).HAVING AVG(salary) > 100000нельзя вWHERE— на моментWHEREагрегата ещё не существует.
Эквивалентная форма через подзапрос
SELECT department, avg_salary, headcount
FROM (
SELECT department, AVG(salary) AS avg_salary, COUNT(*) AS headcount
FROM Employees
WHERE salary >= 30000
GROUP BY department
) t
WHERE avg_salary > 100000;Подводные камни
HAVINGбезGROUP BYработает, но эквивалентенWHEREс агрегатом по всей таблице. Часто это сигнал ошибки.WHERE AVG(salary) > 100000— синтаксическая ошибка во всех нормальных СУБД.HAVING department = 'IT'работает, но логически принадлежитWHERE. Не оптимально, но валидно.HAVINGпосле оконных функций — оконные функции не считаются агрегатами в смыслеHAVING. Используйте подзапрос/CTE.SELECTалиасы вHAVING. В Postgres — можно (HAVING avg_salary > 100000), в стандарте — нет. Безопаснее повторять выражение.
Эталонный ответ
WHERE — фильтр строк до агрегации. HAVING — фильтр групп после агрегации. Условие на отдельные значения колонок — всегда в WHERE. Условие на агрегаты (AVG, SUM, COUNT) — только в HAVING.