SQLLab
📊

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

GROUP BY — один из самых используемых операторов в аналитических запросах. Позволяет считать метрики по группам: выручка по месяцам, число заказов по пользователям, средний чек по категориям.

Как работает GROUP BY

GROUP BY разбивает строки таблицы на группы по одной или нескольким колонкам. К каждой группе применяется агрегатная функция.

Правило: в SELECT можно использовать только те колонки, которые указаны в GROUP BY, или агрегатные функции.

Базовая агрегация
-- Выручка по каждому пользователю
SELECT user_id, SUM(amount) AS total
FROM orders
GROUP BY user_id;

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

COUNT(*) — количество строк в группе. COUNT(col) — количество строк, где col не NULL. SUM(col) — сумма значений. AVG(col) — среднее значение. MAX(col) / MIN(col) — максимум и минимум.

Несколько агрегатных функций
SELECT
  category,
  COUNT(*)          AS products_count,
  AVG(price)        AS avg_price,
  MAX(price)        AS max_price,
  SUM(sales_count)  AS total_sales
FROM products
GROUP BY category
ORDER BY total_sales DESC;

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

WHERE фильтрует строки до группировки. HAVING фильтрует уже готовые группы.

Правило: если условие использует агрегатную функцию (SUM, COUNT, AVG) — используй HAVING. Если нет — WHERE.

WHERE vs HAVING
-- Пользователи с суммой заказов > 10 000
SELECT user_id, SUM(amount) AS total
FROM orders
GROUP BY user_id
HAVING SUM(amount) > 10000;

-- Ошибка: нельзя использовать алиас в HAVING в большинстве СУБД
-- HAVING total > 10000; -- не работает в PostgreSQL

GROUP BY по нескольким колонкам

Можно группировать по нескольким колонкам. Тогда группа = уникальная комбинация значений.

Многоуровневая группировка
-- Выручка по пользователю и категории товара
SELECT
  o.user_id,
  p.category,
  SUM(o.amount) AS revenue
FROM orders o
JOIN products p ON o.product_id = p.id
GROUP BY o.user_id, p.category
ORDER BY revenue DESC;

GROUP BY с датами

Частая задача в аналитике — метрики по периодам. Используй DATE_TRUNC или EXTRACT для группировки по месяцу, неделе, году.

Динамика выручки по месяцам
-- Выручка по месяцам
SELECT
  DATE_TRUNC('month', created_at) AS month,
  SUM(amount)                     AS revenue,
  COUNT(*)                        AS orders_count
FROM orders
GROUP BY DATE_TRUNC('month', created_at)
ORDER BY month;

Типичные ошибки

1. Не включил колонку в GROUP BY: SELECT user_id, name, COUNT(*) ... GROUP BY user_id — ошибка, name не в GROUP BY. 2. Перепутал WHERE и HAVING: WHERE SUM(amount) > 1000 — синтаксическая ошибка. 3. COUNT(*) vs COUNT(col): COUNT(*) считает все строки включая NULL, COUNT(col) — только не-NULL.

Закрепи знания на практике

Решай реальные задачи с собеседований прямо в браузере — без установки.

Решить задачи на GROUP BY