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

↑ 1.1 Реляционная модель и SQL · ← Предыдущая · → Следующая

Обязательно~3 мин чтенияУровень: junior

Зачем это на собесе

Аналитика по данным — типовая задача; частая ловушка — WHERE против HAVING и NULL в агрегатах.

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

ФункцияЗамечания
count(*)все строки
count(col)не-NULL значения
count(DISTINCT col)уникальные не-NULL значения
sum, avg, min, maxигнорируют NULL
string_agg(col, ', ' ORDER BY col)конкатенация
array_agg(col), jsonb_agg(...)сбор в массив
bool_and, bool_orлогические
percentile_cont(0.5) WITHIN GROUP (ORDER BY x)медиана
SELECT customer_id,
       count(*)                         AS orders,
       sum(total)                       AS revenue,
       round(avg(total), 2)             AS avg_check,
       max(created_at)                  AS last_order,
       count(*) FILTER (WHERE status = 'cancelled') AS cancelled
FROM orders
WHERE created_at >= date '2026-01-01'
GROUP BY customer_id
HAVING sum(total) > 10000
ORDER BY revenue DESC;

WHERE и HAVING

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

Условие, не зависящее от агрегатов, должно быть в WHERE: эффективнее и может использовать индексы.

Правило GROUP BY

Каждое выражение в SELECT, не обёрнутое агрегатом, должно быть в GROUP BY (или функционально зависеть от первичного ключа сгруппированной таблицы в PostgreSQL).

Продвинутая группировка

SELECT region, product, sum(amount)
FROM sales
GROUP BY GROUPING SETS ((region, product), (region), ());   -- детализация и итоги
 
GROUP BY ROLLUP (year, month)       -- иерархические подытоги
GROUP BY CUBE (region, product)     -- все комбинации

Группировка по времени

SELECT date_trunc('day', created_at) AS day, count(*)
FROM orders GROUP BY 1 ORDER BY 1;

Для непрерывного ряда с нулями: generate_series + LEFT JOIN.

NULL и агрегаты

  • sum пустого набора возвращает NULL, а не 0: coalesce(sum(x), 0);
  • avg игнорирует NULL: среднее по 3 значениям из 5;
  • count(*) считает строки, count(col) — не-NULL.

Вопросы с ответами