Агрегатные функции
Агрегатная функция сворачивает набор строк в одно значение для всего запроса или каждой группы.
| Функция | Смысл | Поведение с NULL |
|---|---|---|
COUNT(*) | число строк | считает все строки |
COUNT(expr) | число известных значений | пропускает NULL |
SUM(expr) | сумма | пропускает NULL |
AVG(expr) | среднее | пропускает NULL |
MIN, MAX | минимум, максимум | пропускают NULL |
SELECT user_id,
COUNT(*) AS all_orders,
COUNT(total) AS orders_with_total,
COALESCE(SUM(total), 0) AS amount
FROM orders
GROUP BY user_id;SUM по пустому набору обычно возвращает NULL, а не ноль. Если домен требует ноль, это нужно выразить через COALESCE.
Условная агрегация
Стандартный вариант:
SELECT user_id,
SUM(CASE WHEN state = 'PAID' THEN 1 ELSE 0 END) AS paid_count,
SUM(CASE WHEN state = 'NEW' THEN 1 ELSE 0 END) AS new_count
FROM orders
GROUP BY user_id;В некоторых СУБД есть синтаксис FILTER, но CASE переносимее.
DISTINCT внутри агрегата
SELECT COUNT(DISTINCT user_id)
FROM orders
WHERE state = 'PAID';Это количество пользователей, а не заказов. DISTINCT может потребовать сортировки или хеш-таблицы, поэтому его стоимость стоит смотреть в плане.
Точность
Для денег используют точный десятичный тип NUMERIC/DECIMAL, а не двоичный FLOAT. Даже при точном типе нужно заранее определить правила округления и распределения остатка.
Ловушки
- После
LEFT JOINвыражениеCOUNT(*)минимум равно одной строке для левой записи; для числа дочерних объектов считайCOUNT(child.id). AVG(AVG(...))обычно неверно для групп разного размера; нужны сумма и количество.- Агрегация после двух независимых связей «один ко многим» может дать декартово размножение.
Связано: Оператор GROUP BY, Оператор HAVING, Исключение дубликатов, DISTINCT.
Вопросы
Чем COUNT(*) отличается от COUNT(column)? Первый считает строки, второй — ненулевые значения.
Что вернет SUM по пустому набору? Обычно NULL; ноль задают явно через COALESCE.
Почему нельзя усреднять средние? Группы могут иметь разный вес.
Как считать дочерние записи после LEFT JOIN? COUNT(child.id), а не COUNT(*).