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 либо агрегаты.
  • Можно группировать по нескольким колонкам — группа определяется их комбинацией.
Проверьте себя
1. Что можно ставить в SELECT вместе с агрегатами при GROUP BY?
AЛюбые колонки
BТолько колонки из GROUP BY или сами агрегаты
CТолько числовые колонки
DНичего, кроме агрегатов
2. Что вернёт AVG(amount)?
AСумму значений
BСреднее арифметическое значений группы
CКоличество строк
DМаксимум
3. Как определяется группа при GROUP BY city, year?
AТолько по city
BКомбинацией значений (city, year)
CТолько по year
DСлучайно
Поддержать проект