Собесов

Хабр SQL — разница WHERE и HAVING на примере

SQLБазовый синтаксисЛёгкаяJunior

Условие

Таблица 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;

Подводные камни

  1. HAVING без GROUP BY работает, но эквивалентен WHERE с агрегатом по всей таблице. Часто это сигнал ошибки.
  2. WHERE AVG(salary) > 100000 — синтаксическая ошибка во всех нормальных СУБД.
  3. HAVING department = 'IT' работает, но логически принадлежит WHERE. Не оптимально, но валидно.
  4. HAVING после оконных функций — оконные функции не считаются агрегатами в смысле HAVING. Используйте подзапрос/CTE.
  5. SELECT алиасы в HAVING. В Postgres — можно (HAVING avg_salary > 100000), в стандарте — нет. Безопаснее повторять выражение.

Эталонный ответ

WHERE — фильтр строк до агрегации. HAVING — фильтр групп после агрегации. Условие на отдельные значения колонок — всегда в WHERE. Условие на агрегаты (AVG, SUM, COUNT) — только в HAVING.

Хочешь увидеть разбор?

Зарегистрируйся бесплатно — откроется развёрнутое решение этой задачи и ещё 4 на выбор.

Зарегистрироваться и увидеть разбор
Уже есть аккаунт? Войти