Свернуть много строк в одно число на группу. Это ровно то место, где ты в диагностике «забывал GROUP BY». Разберёмся так, чтобы забыть было нельзя.
Поймёшь золотое правило: любой столбец в SELECT, который не внутри агрегата, обязан быть в
GROUP BY. Научишься считать по группам (COUNT, SUM, AVG…),
фильтровать группы через HAVING и видеть разницу между WHERE и HAVING.
Обычный SELECT отдаёт строку на строку. Агрегатная функция берёт много строк и возвращает
одно значение. Без GROUP BY она сворачивает всю таблицу в одну строку:
| Функция | Что считает | Заметка |
|---|---|---|
COUNT(*) | сколько строк | считает все строки группы |
COUNT(col) | сколько не-NULL значений в столбце | NULL не считается |
SUM(col) | сумма | только числа |
AVG(col) | среднее | часто оборачивают в ROUND(…, 2) |
MIN(col) / MAX(col) | минимум / максимум | работает и с датами, и с текстом |
Как только в SELECT появляется агрегат вместе с обычным столбцом, Postgres требует сгруппировать по этому столбцу.
Вот тот самый твой пробел на живом примере — запусти как есть, увидишь ошибку:
Postgres ответит примерно так: column "products.category_id" must appear in the GROUP BY clause….
Логика простая: ты просишь одно число (COUNT) и при этом столбец category_id, у которого
много разных значений. Postgres не знает, какое показать — пока ты не скажешь «по одному числу на каждую категорию».
Добавь GROUP BY category_id — и всё встаёт на места:
Правило: каждый столбец из SELECT, который не завёрнут в агрегат, должен стоять в GROUP BY.
Забыл — получил ту самую ошибку. Теперь ты её знаешь в лицо.
Проверка: SELECT category_id, COUNT(*) FROM products; — без GROUP BY. Что сделает Postgres?
category_id с COUNT(*) без
GROUP BY нельзя — Postgres даёт ошибку «must appear in the GROUP BY clause».
Либо группируй по category_id, либо убери его из SELECT.Как и в Теме 01, важен реальный порядок. Для агрегаций он такой:
FROM / JOIN → собрать строки WHERE → отфильтровать строки (до группировки) GROUP BY → собрать строки в группы HAVING → отфильтровать группы (после агрегации) SELECT → вычислить агрегаты и столбцы ORDER BY → отсортировать результат
Отсюда всё главное про эти два фильтра:
WHERE отсекает строки ещё до группировки. В нём нельзя писать агрегаты (их ещё не посчитали).HAVING отсекает целые группы после подсчёта. В нём можно и нужно писать агрегаты:
HAVING COUNT(*) > 3.Правило-подсказка: условие на отдельную строку → WHERE. Условие на итог группы → HAVING.
Проверка: где отфильтровать «оставить только группы, где COUNT(*) > 3»?
COUNT(*)) существует только после GROUP BY,
поэтому фильтр по нему живёт в HAVING. В WHERE агрегата ещё нет — там фильтруют исходные строки.Иногда в одной строке нужно несколько «условных» подсчётов. В Postgres есть красивый приём — FILTER:
SELECT COUNT(*) AS total,
COUNT(*) FILTER (WHERE status = 'paid') AS paid
FROM orders;
Это чище, чем городить CASE внутри SUM. Пригодится в упражнении 11.
Пиши запрос, жми «Проверить» (⌘/Ctrl+Enter). Сначала сам — усилие памяти строит навык. Подсказка и решение рядом,
бери их только когда реально застрял. Держи схему под рукой. Задачи идут по нарастанию.
1. Разминка (это ты уже умеешь): сколько заказов в каждом статусе? Выведи status и количество.
2. Сколько всего товаров в таблице products? Одно число (без GROUP BY — сворачиваем всю таблицу).
3. В одной строке выведи самую низкую и самую высокую цену товара (два столбца).
4. Сколько товаров в каждой категории? Выведи category_id и количество.
(Заметь: категория 7 «Garden» не появится — у неё нет товаров. Как её вернуть — тема JOIN вглубь.)
5. Средняя цена товара в каждой категории, округлённая до 2 знаков. Выведи category_id и среднюю цену.
6. Оставь только категории, где больше 3 товаров. Выведи category_id и количество. Фильтр — по итогу группы!
7. Считай только товары в наличии (stock > 0). Для каждой категории выведи, сколько таких товаров,
и оставь только категории, где их 3 или больше. Здесь работают оба фильтра — WHERE и HAVING.
8. В таблице customers одной строкой выведи два числа: сколько всего покупателей и у скольких заполнена
страна (country). Числа разные — подумай почему.
9. Выручка по категориям (та самая задача 6 из диагностики — теперь с опорой). Для каждой категории, где были продажи,
выведи имя категории (name) и суммарную выручку — сумму quantity * unit_price по проданным товарам.
10. Найди покупателей с 2 и более заказами. Выведи их name и число заказов.
11. Одной строкой по всем заказам посчитай три числа: всего заказов, оплачено (status = 'paid')
и отменено (status = 'cancelled'). Используй COUNT(*) FILTER (WHERE …).
12. Капстоун. Только по заказам со статусом paid: для каждого покупателя (name) посчитай его
суммарную выручку (сумма quantity * unit_price по позициям его paid-заказов). Оставь только тех, у кого сумма
больше 100, и отсортируй по сумме по убыванию. (важен порядок)
1) Неагрегированный столбец в SELECT без GROUP BY → ошибка «must appear in the GROUP BY clause».
2) Условие по COUNT/SUM нельзя класть в WHERE — только в HAVING.
3) COUNT(col) молча пропускает NULL; COUNT(*) — нет.