SQLLab
Все статьи

Агрегатные функции SQL: COUNT, SUM, AVG, MIN, MAX

Полный разбор агрегатных функций SQL: COUNT(*) vs COUNT(col), SUM с NULL, AVG и деление на ноль, оконные функции vs агрегация. Примеры для аналитиков.

15 марта 2026 г.·5 мин чтения·

Агрегатные функции превращают набор строк в одно значение. Без них невозможен ни один аналитический запрос — отчёты по продажам, статистика пользователей, финансовые метрики.

Пять основных функций

ФункцияЧто делает
COUNT(*)Считает строки
COUNT(col)Считает строки, где col не NULL
SUM(col)Сумма
AVG(col)Среднее арифметическое
MIN(col)Минимальное значение
MAX(col)Максимальное значение

Пример: таблица orders

iduser_idamountstatuscreated_at
1105000completed2026-01-05
2103200completed2026-01-12
3118900refunded2026-01-20
412NULLpending2026-02-01
5114100completed2026-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) — только не-NULL
  • SUM, AVG, MIN, MAX игнорируют NULL
  • GROUP BY разбивает на группы, HAVING — фильтр по группам
  • При делении используйте NULLIF для защиты от деления на ноль
  • PostgreSQL расширяет стандарт: STRING_AGG, ARRAY_AGG, PERCENTILE_CONT

Похожие статьи

Попробуй на практике

Тренажёр с реальными задачами — бесплатно и без регистрации

Открыть тренажёр →