Агрегаты, GROUP BY и HAVING

Сворачиваем множество строк в итоговые числа и группируем данные по категориям.

Агрегатная функция сворачивает множество строк в одно значение — сумму, среднее, количество — обычно в разрезе групп, заданных GROUP BY.

Агрегатные функции

Пять основных функций отвечают на вопросы «сколько», «на какую сумму», «в среднем».

ФункцияЧто считает
COUNT(*)количество строк
SUM(x)сумму значений
AVG(x)среднее
MIN(x) / MAX(x)минимум / максимум

Считаем по всей таблице

Без группировки агрегат сворачивает всю таблицу в одну строку. Запустите пример.

CREATE TABLE sales (
    id     INTEGER PRIMARY KEY,
    region TEXT,
    amount INTEGER
);

INSERT INTO sales (id, region, amount) VALUES
    (1, 'Север', 100), (2, 'Север', 200), (3, 'Юг', 150),
    (4, 'Юг', 300), (5, 'Юг', 50), (6, 'Север', 400);

SELECT COUNT(*) AS cnt, SUM(amount) AS total, AVG(amount) AS avg_amount
FROM sales;

Вывод:

cnt|total|avg_amount
6|1200|200.0

GROUP BY — считаем по группам

Самое интересное начинается с группировки. GROUP BY region разбивает строки на группы по региону, и агрегат считается внутри каждой.

SELECT region,
       COUNT(*)    AS deals,
       SUM(amount) AS total
FROM sales
GROUP BY region
ORDER BY total DESC;

Вывод:

Север|3|700
Юг|3|500

Ключевое правило: в SELECT при группировке могут быть только столбцы из GROUP BY и агрегаты. Нельзя выбрать id рядом с SUM(amount) — у группы нет одного id.

HAVING — фильтр для групп

WHERE фильтрует строки до группировки, а HAVING — уже готовые группы по их агрегатам. Это разные этапы.

SELECT region, SUM(amount) AS total
FROM sales
GROUP BY region
HAVING SUM(amount) > 600;

Вывод:

Север|700

Группа «Юг» (500) отсеялась, осталась только та, чья сумма больше 600. Запомните порядок: сначала WHERE отбирает строки, потом GROUP BY группирует, и только затем HAVING фильтрует группы.

WHERE и HAVING вместе

-- Считаем только крупные сделки (WHERE), потом отбираем щедрые регионы (HAVING)
SELECT region, COUNT(*) AS big_deals
FROM sales
WHERE amount >= 150
GROUP BY region
HAVING COUNT(*) >= 2;

Вывод:

Юг|2

Итог

  • Агрегаты COUNT, SUM, AVG, MIN, MAX сворачивают строки в одно число.
  • GROUP BY считает агрегат внутри каждой группы; в SELECT допустимы только группирующие столбцы и агрегаты.
  • WHERE фильтрует строки до группировки, HAVING — готовые группы по агрегатам.
Проверьте себя
1. В чём разница между WHERE и HAVING?
AЭто синонимы
BWHERE фильтрует строки до группировки, HAVING — готовые группы по их агрегатам
CHAVING работает быстрее
DWHERE нельзя использовать с GROUP BY
2. Почему нельзя выбрать id рядом с SUM(amount) при GROUP BY region?
Aid — зарезервированное слово
BУ группы нет единственного значения id, только агрегаты и столбцы из GROUP BY допустимы
CSUM не работает с id
DЭто разрешено
3. Что посчитает COUNT(*) в запросе с GROUP BY region?
AОбщее число строк во всей таблице
BЧисло строк в каждой группе (по регионам)
CЧисло регионов
DСумму amount
Поддержать проект