Раздел 10 · Databases

WHERE против HAVING

junior~15 мин

без входаВойди, чтобы отмечать прогресс, вести заметки и решать задачи в редакторе. войти

WHERE и HAVING оба отсекают лишнее, поэтому кандидаты их путают. Интервьюер это знает и спрашивает почти на каждом первом собеседовании по SQL. Хороший ответ занимает две фразы, а держится на порядке выполнения из прошлого урока. Все примеры работают на учебной базе блока.

Как спрашивают

Чем отличается WHERE от HAVING? Можно ли написать условие на агрегат в WHERE?

Иногда в виде задачи: «Найди клиентов, у которых сумма заказов больше 5000».

Короткий ответ

WHERE фильтрует отдельные строки до группировки, а HAVING фильтрует готовые группы после неё. Поэтому агрегатные функции вроде sum и count можно писать в HAVING, но нельзя в WHERE: на шаге WHERE групп ещё нет. Условие, которое не зависит от агрегата, лучше ставить в WHERE: тогда в группировку попадёт меньше строк.

Разбор: сито и весы

Представь сортировку яблок на складе. Сначала рабочий проверяет каждое яблоко поштучно и выкидывает гнилые. Это WHERE: решение принимается про одну строку. Потом оставшиеся яблоки раскладывают по ящикам, по ящику на сорт, и взвешивают ящики целиком. Ящики легче десяти килограммов отправляют обратно. Это HAVING: решение принимается про группу, по её общему весу.

Спросить «сколько весит ящик?» до того, как яблоки разложены, бессмысленно. Ровно поэтому агрегат в WHERE даёт ошибку.

Сумма заказов по клиентам на учебной базе:

SELECT customer_id, sum(amount) AS total
FROM orders
GROUP BY customer_id;
customer_id | total
------------+------
          1 | 11000
          2 |  7000
          3 |  1200

Клиенты с суммой больше 5000. Условие про сумму группы, значит, HAVING:

SELECT customer_id, sum(amount) AS total
FROM orders
GROUP BY customer_id
HAVING sum(amount) > 5000;
customer_id | total
------------+------
          1 | 11000
          2 |  7000

А вот попытка написать то же через WHERE:

SELECT customer_id, sum(amount)
FROM orders
WHERE sum(amount) > 5000
GROUP BY customer_id;

PostgreSQL отвечает: ERROR: aggregate functions are not allowed in WHERE.

Оба фильтра в одном запросе

Чаще всего на собеседовании нужны оба фильтра сразу. Задача: в каких отделах больше одного сотрудника с зарплатой выше 150 000?

SELECT department_id, count(*) AS n
FROM employees
WHERE salary > 150000
GROUP BY department_id
HAVING count(*) > 1;
department_id | n
--------------+--
            1 | 3

Порядок работы такой:

  1. WHERE salary > 150000 отбрасывает Дину и Егора поштучно.
  2. GROUP BY раскладывает остальных: в отделе 1 трое, в отделе 2 один Глеб, и ещё Жанна в группе с department_id равным NULL.
  3. HAVING count(*) > 1 оставляет только группу из трёх человек.

Если поменять фильтры местами по смыслу, получится другой вопрос. WHERE решает, какие строки считать, HAVING решает, какие итоги показывать.

дальше после входа

Урок целиком открыт всем, кто вошёл. Войди по своему email, чтобы читать дальше.

Вход пока по приглашению. Если хочешь учиться, напиши Vassiliy.