Логотип Workflow

Article

Updated at:

Group By Having

Stage 5. GROUP BY и HAVING для бизнес-метрик

Когда бизнес спрашивает “какая выручка?” или “сколько заказов у каждого клиента?”, обычного SELECT недостаточно. Нужна агрегация: превратить много строк в компактные итоги. Для этого используются GROUP BY и HAVING.

Aggregation pipeline

Что такое GROUP BY

GROUP BY объединяет строки в группы по указанному полю (или нескольким полям). После этого по каждой группе можно считать агрегаты: COUNT, SUM, AVG, MIN, MAX.

Пример:

SELECT user_id,
       COUNT(*) AS orders_count,
       SUM(amount) AS total_amount
FROM orders
WHERE status = 'PAID'
GROUP BY user_id;

Что происходит:

  1. Берем только PAID из WHERE.
  2. Делим строки на группы по user_id.
  3. Для каждой группы считаем число заказов и сумму.

Что такое HAVING

HAVING фильтрует уже готовые группы после GROUP BY.

SELECT user_id,
       SUM(amount) AS total_amount
FROM orders
WHERE status = 'PAID'
GROUP BY user_id
HAVING SUM(amount) >= 1000;

Смысл: оставить только тех пользователей, у кого суммарно оплачено не меньше 1000.

Ключевая разница:

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

Термины и ключевые слова в одной таблице

Ключевое словоРольПример
GROUP BYГруппирует строки по ключуGROUP BY user_id
COUNT(*)Считает строки в группеЧисло заказов
SUM(amount)Суммирует значения в группеВыручка
HAVINGОтсеивает группы по условиюHAVING SUM(amount) > 1000

Пример с результатом

SELECT user_id,
       COUNT(*) AS orders_count,
       SUM(amount) AS total_amount
FROM orders
WHERE status = 'PAID'
GROUP BY user_id
ORDER BY total_amount DESC;

Результат:

user_idorders_counttotal_amount
154700.00
221400.00
31300.00

Частые ошибки новичков

  1. Используют HAVING, когда нужен обычный WHERE.
  2. Пытаются выбрать поле в SELECT, которое не агрегировано и не входит в GROUP BY.
  3. Группируют по слишком “точному” полю (например полный timestamp), получая тысячи мелких групп.

Практический сценарий: топ-клиенты за месяц

SELECT user_id,
       SUM(amount) AS revenue
FROM orders
WHERE status = 'PAID'
  AND created_at >= DATE '2026-05-01'
  AND created_at <  DATE '2026-06-01'
GROUP BY user_id
HAVING SUM(amount) >= 1000
ORDER BY revenue DESC;

Это готовая база для отчета “кто сделал наибольшую выручку за период”.

Практика перед следующим уроком

Сначала напишите простой SELECT по оплаченных заказам. Затем добавьте GROUP BY. Потом добавьте HAVING. На каждом шаге проверяйте, как меняется результат: строки → группы → отфильтрованные группы.