Глиф Учебники

Чем WHERE отличается от HAVING

Справочник · SQL · 1 минута чтения

WHERE отбирает строки до группировки, HAVING — группы после неё. Поэтому условия на обычные столбцы пишут в WHERE, а условия на итоги (COUNT, SUM, AVG и другие агрегатные функции) — в HAVING.

Примеры — на таблице продаж:

SQL
CREATE TABLE sales (
    id     INTEGER PRIMARY KEY,
    city   TEXT    NOT NULL,
    amount INTEGER NOT NULL
);

INSERT INTO sales (city, amount) VALUES
    ('Минск', 120), ('Минск', 40), ('Минск', 300),
    ('Гомель', 50), ('Гомель', 20),
    ('Брест', 500);

WHERE — условие на строки

Продажи от 50 рублей, по городам:

SQL
SELECT city, COUNT(*) AS sales, SUM(amount) AS total
FROM sales
WHERE amount >= 50
GROUP BY city
ORDER BY city;
citysalestotal
Брест1500
Гомель150
Минск2420

Продажи на 40 и 20 рублей отброшены ещё до подсчёта — поэтому у Минска сумма 420, а не 460.

HAVING — условие на группы

Города, где продано больше чем на 400 рублей:

SQL
SELECT city, SUM(amount) AS total
FROM sales
GROUP BY city
HAVING SUM(amount) > 400
ORDER BY city;
citytotal
Брест500
Минск460

Гомель с суммой 70 отсеялся. Сумма по городу известна только после группировки, поэтому условие на неё может стоять только в HAVING.

Вместе

Города, где не меньше двух продаж от 50 рублей:

SQL
SELECT city, COUNT(*) AS big_sales
FROM sales
WHERE amount >= 50
GROUP BY city
HAVING COUNT(*) >= 2;
citybig_sales
Минск2

Порядок выполнения

  1. FROM — берётся таблица.
  2. WHERE — отбрасываются строки.
  3. GROUP BY — оставшиеся строки собираются в группы.
  4. HAVING — отбрасываются группы.
  5. SELECT — считаются столбцы ответа.
  6. ORDER BY и LIMIT — сортировка и обрезка.

Из этого порядка следуют все правила ниже.

Частые ошибки

Агрегатная функция в WHERE. WHERE SUM(amount) > 400 — ошибка в любой СУБД, ведь на шаге WHERE групп ещё нет:

СУБДСообщение
SQLitemisuse of aggregate: SUM()
MySQLInvalid use of group function
PostgreSQLaggregate functions are not allowed in WHERE

Обычное условие в HAVING. HAVING city = 'Минск' сработает, но СУБД сначала сгруппирует все города, а потом выбросит лишние. В WHERE то же условие отсечёт строки сразу — запрос понятнее и обычно быстрее.

Псевдоним в HAVING. SQLite и MySQL понимают HAVING total > 400, где total — имя из SELECT. PostgreSQL выдаст ошибку column "total" does not exist. Пишите в HAVING само выражение — SUM(amount) > 400, так работает везде.

HAVING без GROUP BY. Тогда вся таблица — одна группа: SELECT COUNT(*) FROM sales HAVING COUNT(*) > 5 вернёт одну строку или ни одной. Это допустимо (в SQLite — с версии 3.39), но встречается редко.

Подробнее о группировке — в уроке Подсчёт и группировка.