GROUP BY и HAVING в SQL: как агрегировать данные для аналитики

GROUP BY и HAVING без путаницы: как агрегировать данные для аналитики, зачем нужен второй фильтр после агрегации и где чаще всего ошибаются.

Содержание Следующие статьи
Содержание GROUP BY и HAVING в SQL: как агрегировать данные для аналитики
  1. Почему агрегация — это один из главных навыков в аналитике
  2. Что именно делает агрегация
  3. Почему HAVING нужен после GROUP BY
  4. Как увидеть это в Python-аналогии

Почему агрегация — это один из главных навыков в аналитике

Очень большая часть аналитической работы начинается с вопроса не про отдельную строку, а про группу объектов. Сколько заказов у пользователя, какая средняя выручка по стране, сколько ошибок на сервис по неделям, какой retention у сегмента. Именно поэтому `GROUP BY` — одна из самых фундаментальных конструкций в SQL. Она позволяет перейти от событий к сжатому, управляемому представлению данных.

Но начинающие часто воспринимают `GROUP BY` как механический синтаксис, а не как способ переописать уровень детализации задачи. На практике это важнее всего: после агрегации таблица уже рассказывает другую историю.

Что именно делает агрегация

Формула: раздел математики — дискретная математика и агрегирующие операции
$$ group\_metric(g) = f(x_1, x_2, \ldots, x_n) $$
Что означает эта формула

Для каждой группы g мы берем набор строк и применяем к нему агрегирующее правило: сумму, среднее, число событий, максимум и так далее. В результате вместо множества строк появляется одна сводная характеристика группы.

Что означает каждый символ
  • group\_metric(g) — итоговая метрика для группы g
  • f — агрегирующая функция, например sum или avg
  • x_1 \ldots x_n — строки, которые попали в эту группу

Этот взгляд полезен тем, что заставляет не просто писать `count(*)`, а понимать, какой объект анализа возникает после группировки: пользователь, день, категория, сегмент, страна.

Почему HAVING нужен после GROUP BY

`WHERE` фильтрует строки до агрегации. `HAVING` фильтрует уже сами группы после того, как агрегаты посчитаны. Это простая идея, но именно здесь часто возникает путаница. Например, если хочется оставить только тех пользователей, у кого суммарная выручка выше определенного порога, `WHERE` уже не подойдет — такого поля еще не существует до агрегации. Нужен именно `HAVING`.

То есть `HAVING` — это не редкий экзотический оператор, а естественное продолжение группировки, когда нас интересует отбор уже на уровне агрегированных объектов.

Как увидеть это в Python-аналогии

example.pyPython
import pandas as pd  # создаем таблицу заказов для демонстрации группировки

orders = pd.DataFrame({  # задаем пользователя и сумму заказа
    'user_id': [1, 1, 2, 2, 2, 3],  # ключ пользователя для будущей группировки
    'amount': [500, 900, 200, 300, 700, 150],  # суммы отдельных заказов
})
agg = orders.groupby('user_id', as_index=False).agg(  # агрегируем данные по пользователю
    orders_cnt=('amount', 'size'),  # считаем число заказов в группе
    revenue_sum=('amount', 'sum'),  # считаем суммарную выручку по пользователю
) 
filtered = agg[agg['revenue_sum'] >= 1000]  # имитируем HAVING после агрегации
print(filtered)  # смотрим только те группы, что прошли условие по агрегату

Очень важно почувствовать разницу: сначала мы строим новый уровень данных, а уже потом фильтруем его. Именно это и делает `GROUP BY` плюс `HAVING` одним из базовых инструментов аналитического мышления.

Что читать дальше

Связанные статьи по этой теме

Canary deployment для моделей: как выкатывать новую версию без лишнего риска Latency в ML API: почему быстрая модель важна не меньше точной Batch inference и real-time inference: как выбирать режим работы модели
Вернуться в блог