Чем WHERE отличается от HAVING
WHERE отбирает строки до группировки, HAVING — группы после неё. Поэтому условия на обычные столбцы пишут в WHERE, а условия на итоги (COUNT, SUM, AVG и другие агрегатные функции) — в HAVING.
Примеры — на таблице продаж:
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 рублей, по городам:
SELECT city, COUNT(*) AS sales, SUM(amount) AS total
FROM sales
WHERE amount >= 50
GROUP BY city
ORDER BY city;| city | sales | total |
|---|---|---|
| Брест | 1 | 500 |
| Гомель | 1 | 50 |
| Минск | 2 | 420 |
Продажи на 40 и 20 рублей отброшены ещё до подсчёта — поэтому у Минска сумма 420, а не 460.
HAVING — условие на группы
Города, где продано больше чем на 400 рублей:
SELECT city, SUM(amount) AS total
FROM sales
GROUP BY city
HAVING SUM(amount) > 400
ORDER BY city;| city | total |
|---|---|
| Брест | 500 |
| Минск | 460 |
Гомель с суммой 70 отсеялся. Сумма по городу известна только после группировки, поэтому условие на неё может стоять только в HAVING.
Вместе
Города, где не меньше двух продаж от 50 рублей:
SELECT city, COUNT(*) AS big_sales
FROM sales
WHERE amount >= 50
GROUP BY city
HAVING COUNT(*) >= 2;| city | big_sales |
|---|---|
| Минск | 2 |
Порядок выполнения
FROM— берётся таблица.WHERE— отбрасываются строки.GROUP BY— оставшиеся строки собираются в группы.HAVING— отбрасываются группы.SELECT— считаются столбцы ответа.ORDER BYиLIMIT— сортировка и обрезка.
Из этого порядка следуют все правила ниже.
Частые ошибки
Агрегатная функция в WHERE. WHERE SUM(amount) > 400 — ошибка в любой СУБД, ведь на шаге WHERE групп ещё нет:
| СУБД | Сообщение |
|---|---|
| SQLite | misuse of aggregate: SUM() |
| MySQL | Invalid use of group function |
| PostgreSQL | aggregate 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), но встречается редко.
Подробнее о группировке — в уроке Подсчёт и группировка.