GROUP BY и агрегатные функции
GROUP BY превращает строки в группы, а агрегаты считают по каждой группе одно число.
GROUP BY собирает строки с одинаковыми значениями в группы, а агрегатные функции (
SUM,COUNT,AVG,MIN,MAX) сводят каждую группу к одному значению.
Зачем нужна группировка
«Сколько заказов у каждого клиента?», «Средний чек по городам?» — везде, где звучит «по каждому/по группам», нужен GROUP BY. Он делит таблицу на группы, и агрегат считает итог отдельно для каждой.
CREATE TABLE sales (
id INTEGER PRIMARY KEY,
city TEXT,
amount INTEGER
);
INSERT INTO sales (city, amount) VALUES
('Москва', 500),
('Москва', 300),
('Казань', 200),
('Казань', 400),
('Казань', 300);
SELECT city,
COUNT(*) AS chislo_prodazh,
SUM(amount) AS vyruchka,
CAST(AVG(amount) AS INTEGER) AS sredniy_chek, -- округляем до целого
MAX(amount) AS maks
FROM sales
GROUP BY city
ORDER BY vyruchka DESC;
Вывод:
Казань|3|900|300|400 Москва|2|800|400|500
По каждому городу — отдельная строка с агрегатами. Москва: 2 продажи, выручка 800, средний чек 400. Казань: 3 продажи, выручка 900, средний чек 300. Мы обернули AVG в CAST(... AS INTEGER), чтобы средний чек показывался целым числом; без него AVG вернул бы дробь.
Главное правило GROUP BY
В SELECT вместе с агрегатами можно ставить только колонки из GROUP BY. Логика проста: группа — это одна строка результата, и «обычная» колонка, не входящая в группировку, имела бы в группе несколько разных значений — какое показать, непонятно.
В строгих СУБД (PostgreSQL) такой запрос — ошибка. SQLite его выполнит, но возьмёт произвольное значение из группы, что почти всегда баг:
-- В PostgreSQL это ОШИБКА: amount не в GROUP BY и не под агрегатом
SELECT city, amount, SUM(amount)
FROM sales
GROUP BY city;
-- column "sales.amount" must appear in the GROUP BY clause
-- or be used in an aggregate function
Правило: всё, что в SELECT при группировке, должно быть либо в GROUP BY, либо внутри агрегатной функции.
Группировка по нескольким колонкам
Можно группировать сразу по нескольким полям — тогда группа определяется комбинацией значений:
CREATE TABLE sales (
id INTEGER PRIMARY KEY,
city TEXT,
year INTEGER,
amount INTEGER
);
INSERT INTO sales (city, year, amount) VALUES
('Москва', 2023, 500),
('Москва', 2024, 300),
('Москва', 2024, 200),
('Казань', 2024, 100);
SELECT city, year, SUM(amount) AS vyruchka
FROM sales
GROUP BY city, year
ORDER BY city, year;
Вывод:
Казань|2024|100 Москва|2023|500 Москва|2024|500
Группа — это пара (город, год). У Москвы за 2024 две продажи свернулись в одну строку с суммой 500.
Итог
GROUP BYделит строки на группы, агрегат считает по каждой одно значение.- В
SELECTпри группировке — только колонки изGROUP BYлибо агрегаты. - Можно группировать по нескольким колонкам — группа определяется их комбинацией.