Фильтруй после агрегации с помощью HAVING и разворачивай данные через CASE WHEN внутри агрегатных функций.
Открыть этот урок в KodokonТы уже знаешь GROUP BY. Этот модуль разбирает то, что отличает просто корректный запрос от профессионального: фильтрацию после агрегации, условные агрегаты и уверенное понимание порядка вычисления. Чтобы потренироваться, открой sqliteonline.com (движок SQLite) или запусти sqlite3 в терминале: в каждом уроке есть нужные тебе инструкции CREATE TABLE и INSERT. Скопируй этот набор данных, прежде чем запускать примеры.
CREATE TABLE orders (
id INTEGER PRIMARY KEY,
customer TEXT NOT NULL,
category TEXT NOT NULL,
amount REAL NOT NULL,
status TEXT NOT NULL
);
INSERT INTO orders (customer, category, amount, status)
VALUES
('alice', 'books', 42.0, 'paid'),
('alice', 'games', 55.0, 'paid'),
('alice', 'books', 18.5, 'refunded'),
('bruno', 'games', 70.0, 'paid'),
('bruno', 'books', 12.0, 'paid'),
('chloe', 'games', 95.0, 'paid'),
('chloe', 'books', 30.0, 'paid'),
('david', 'books', 8.0, 'refunded');Логический порядок вычисления - вот ключ: FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY. WHERE фильтрует строки до группировки; HAVING фильтрует группы после того, как агрегаты уже посчитаны. Именно поэтому WHERE SUM(amount) > 90 недопустим: на этапе WHERE никакой суммы ещё не существует. Здесь мы оставляем только клиентов, чья оплаченная сумма превышает 90 (ожидаем: alice и chloe).
SELECT customer,
COUNT(*) AS nb_orders,
SUM(amount) AS total
FROM orders
WHERE status = 'paid'
GROUP BY customer
HAVING SUM(amount) > 90;Второй обязательный паттерн - условный агрегат. Подставив CASE WHEN внутрь SUM или COUNT, ты считаешь сразу несколько метрик за один проход по таблице там, где новичок запустил бы три отдельных запроса. Это стандартный приём, чтобы развернуть строки в колонки.
SELECT customer,
SUM(CASE WHEN category = 'books'
THEN amount ELSE 0 END) AS books_total,
SUM(CASE WHEN category = 'games'
THEN amount ELSE 0 END) AS games_total
FROM orders
WHERE status = 'paid'
GROUP BY customer;