Агрегатные функции превращают набор строк в одно значение. Без них невозможен ни один аналитический запрос — отчёты по продажам, статистика пользователей, финансовые метрики.
Пять основных функций
| Функция | Что делает |
|---|---|
COUNT(*) | Считает строки |
COUNT(col) | Считает строки, где col не NULL |
SUM(col) | Сумма |
AVG(col) | Среднее арифметическое |
MIN(col) | Минимальное значение |
MAX(col) | Максимальное значение |
Пример: таблица orders
| id | user_id | amount | status | created_at |
|---|---|---|---|---|
| 1 | 10 | 5000 | completed | 2026-01-05 |
| 2 | 10 | 3200 | completed | 2026-01-12 |
| 3 | 11 | 8900 | refunded | 2026-01-20 |
| 4 | 12 | NULL | pending | 2026-02-01 |
| 5 | 11 | 4100 | completed | 2026-02-15 |
COUNT: считаем строки
-- Общее число заказов
SELECT COUNT(*) FROM orders;
-- Результат: 5
-- Заказы с указанной суммой (исключает NULL)
SELECT COUNT(amount) FROM orders;
-- Результат: 4 (заказ 4 не считается, amount = NULL)
-- Уникальные пользователи, сделавшие заказ
SELECT COUNT(DISTINCT user_id) FROM orders;
-- Результат: 3
COUNT(*) vs COUNT(col)
COUNT(*) считает все строки, включая NULL.
COUNT(col) считает строки, где col не NULL.
SELECT
COUNT(*) AS total_rows, -- 5
COUNT(amount) AS rows_with_amount, -- 4
COUNT(status) AS rows_with_status -- 5 (нет NULL в status)
FROM orders;
На собеседованиях часто спрашивают именно эту разницу.
SUM: сумма значений
SELECT SUM(amount) AS total_revenue FROM orders;
-- SUM игнорирует NULL: 5000 + 3200 + 8900 + 4100 = 21200
SUM с фильтром
SELECT SUM(amount) AS completed_revenue
FROM orders
WHERE status = 'completed';
-- 5000 + 3200 + 4100 = 12300
SUM и NULL
SUM игнорирует NULL-значения. Если все значения NULL, вернётся NULL (не 0):
SELECT SUM(amount) FROM orders WHERE user_id = 99;
-- Нет таких строк → NULL, не 0
Для получения 0 вместо NULL:
SELECT COALESCE(SUM(amount), 0) AS revenue FROM orders WHERE user_id = 99;
AVG: среднее значение
SELECT AVG(amount) AS avg_order FROM orders;
-- (5000 + 3200 + 8900 + 4100) / 4 = 5300
-- NULL в знаменатель и числитель не включается
Деление на ноль в AVG
Если строк нет, AVG вернёт NULL — не вызовет ошибку. Это безопасно.
Взвешенное среднее
AVG — простое среднее. Взвешенное нужно считать вручную:
-- Средний чек, взвешенный по количеству товаров
SELECT SUM(amount * quantity) / NULLIF(SUM(quantity), 0) AS weighted_avg
FROM order_items;
NULLIF(SUM(quantity), 0) защищает от деления на ноль.
MIN и MAX
SELECT
MIN(amount) AS min_order, -- 3200
MAX(amount) AS max_order, -- 8900
MIN(created_at) AS first_order,
MAX(created_at) AS last_order
FROM orders
WHERE status = 'completed';
MIN и MAX работают не только с числами, но и со строками (лексикографически) и датами.
Агрегация по группам: GROUP BY
Агрегатные функции обычно используются с GROUP BY:
-- Выручка и число заказов по каждому пользователю
SELECT
user_id,
COUNT(*) AS order_count,
SUM(amount) AS total_spent,
AVG(amount) AS avg_order,
MAX(amount) AS biggest_order
FROM orders
WHERE status = 'completed'
GROUP BY user_id
ORDER BY total_spent DESC;
HAVING — фильтр после агрегации
WHERE фильтрует строки до агрегации, HAVING — после:
-- Только пользователи с суммой покупок > 5000
SELECT user_id, SUM(amount) AS total
FROM orders
GROUP BY user_id
HAVING SUM(amount) > 5000;
-- Комбинация WHERE + HAVING
SELECT user_id, COUNT(*) AS cnt
FROM orders
WHERE status = 'completed' -- до агрегации
GROUP BY user_id
HAVING COUNT(*) >= 2; -- после агрегации
Агрегация без GROUP BY
Если нет GROUP BY — вся таблица одна группа:
SELECT COUNT(*), SUM(amount), MIN(created_at)
FROM orders;
-- Одна строка с итогами по всей таблице
Дополнительные агрегатные функции в PostgreSQL
PostgreSQL поддерживает расширенные агрегаты:
-- Строковая конкатенация
SELECT user_id, STRING_AGG(status, ', ') AS statuses
FROM orders
GROUP BY user_id;
-- Массив значений
SELECT user_id, ARRAY_AGG(amount ORDER BY created_at) AS amounts
FROM orders
GROUP BY user_id;
-- Процентиль (медиана)
SELECT PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY amount) AS median
FROM orders;
-- Стандартное отклонение
SELECT STDDEV(amount) AS stddev, VARIANCE(amount) AS variance FROM orders;
Агрегатные функции vs оконные функции
Агрегатные функции сворачивают строки:
SELECT user_id, SUM(amount) FROM orders GROUP BY user_id;
-- Для 5 строк с 3 user_id → 3 строки
Оконные функции сохраняют строки, добавляя агрегат:
SELECT *, SUM(amount) OVER (PARTITION BY user_id) AS user_total
FROM orders;
-- Все 5 строк, но с колонкой user_total в каждой
Подробнее — в статье про оконные функции.
Частые ошибки на собеседованиях
1. Столбец вне GROUP BY:
-- Ошибка: name не в GROUP BY и не в агрегате
SELECT user_id, name, SUM(amount)
FROM orders
GROUP BY user_id;
2. WHERE вместо HAVING:
-- Ошибка: нельзя фильтровать по агрегату в WHERE
SELECT user_id, SUM(amount) FROM orders
WHERE SUM(amount) > 5000
GROUP BY user_id;
-- Правильно: HAVING
HAVING SUM(amount) > 5000
3. NULL в COUNT:
-- COUNT(*) = 100, COUNT(optional_field) = 73 — разные числа!
Итог
Агрегатные функции — основа аналитики данных. Ключевые правила:
COUNT(*)считает все строки,COUNT(col)— только не-NULLSUM,AVG,MIN,MAXигнорируют NULLGROUP BYразбивает на группы,HAVING— фильтр по группам- При делении используйте
NULLIFдля защиты от деления на ноль - PostgreSQL расширяет стандарт:
STRING_AGG,ARRAY_AGG,PERCENTILE_CONT