Агрегатные функции — инструмент для вычислений по группам строк. Без них невозможно посчитать выручку, найти среднее или найти максимальное значение.
Основные функции
COUNT — подсчёт строк
SELECT COUNT(*) FROM orders; -- все строки
SELECT COUNT(email) FROM users; -- строки где email не NULL
SELECT COUNT(DISTINCT city) FROM users; -- уникальные города
COUNT(*) считает все строки включая NULL. COUNT(колонка) — только строки где значение не NULL.
SUM — сумма
SELECT SUM(amount) FROM orders;
SELECT SUM(amount) FROM orders WHERE status = 'paid';
Игнорирует NULL. Если все значения NULL — возвращает NULL.
AVG — среднее значение
SELECT AVG(price) FROM products;
SELECT ROUND(AVG(salary), 2) FROM employees;
Тоже игнорирует NULL. Это важно: AVG(10, NULL, 20) = 15, а не 10.
MIN / MAX — минимум и максимум
SELECT MIN(price), MAX(price) FROM products;
SELECT MIN(created_at) AS first_order FROM orders;
SELECT MAX(created_at) AS last_order FROM orders;
Работают с числами, датами и строками (лексикографически).
GROUP BY — группировка
Агрегатные функции становятся по-настоящему полезными с GROUP BY:
-- Выручка по категориям
SELECT category, SUM(price) AS revenue
FROM products
GROUP BY category;
-- Статистика заказов по пользователям
SELECT
user_id,
COUNT(*) AS total_orders,
SUM(amount) AS total_spent,
AVG(amount) AS avg_order,
MIN(created_at) AS first_order,
MAX(created_at) AS last_order
FROM orders
GROUP BY user_id;
HAVING — фильтрация групп
WHERE фильтрует строки до GROUP BY. HAVING — после:
-- Только пользователи с более чем 5 заказами
SELECT user_id, COUNT(*) AS orders
FROM orders
GROUP BY user_id
HAVING COUNT(*) > 5;
-- Категории с выручкой больше 100 000
SELECT category, SUM(amount) AS revenue
FROM orders
GROUP BY category
HAVING SUM(amount) > 100000
ORDER BY revenue DESC;
Комбинированные вычисления
SELECT
category,
COUNT(*) AS products_count,
SUM(price) AS total_revenue,
ROUND(AVG(price), 0) AS avg_price,
MAX(price) - MIN(price) AS price_range,
ROUND(100.0 * SUM(price) / SUM(SUM(price)) OVER (), 1) AS revenue_share_pct
FROM products
GROUP BY category
ORDER BY total_revenue DESC;
Условная агрегация (CASE WHEN внутри функции)
Мощный приём — считать только строки удовлетворяющие условию:
SELECT
COUNT(*) AS total,
COUNT(CASE WHEN status = 'paid' THEN 1 END) AS paid,
COUNT(CASE WHEN status = 'cancelled' THEN 1 END) AS cancelled,
SUM(CASE WHEN status = 'paid' THEN amount ELSE 0 END) AS paid_revenue
FROM orders;
Агрегаты с NULL
-- Таблица: 10, 20, NULL, 30
SELECT
COUNT(*) AS total_rows, -- 4
COUNT(value) AS non_null, -- 3
SUM(value) AS sum, -- 60 (NULL игнорируется)
AVG(value) AS avg; -- 20 (60/3, не 60/4!)
STRING_AGG — объединить строки
-- Список сотрудников по отделам
SELECT
department,
STRING_AGG(name, ', ' ORDER BY name) AS employees
FROM employees
GROUP BY department;
Порядок выполнения
FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT
Поэтому:
- В WHERE нельзя использовать алиасы из SELECT
- В HAVING можно использовать агрегаты
- В ORDER BY можно использовать алиасы из SELECT
SELECT department, COUNT(*) AS emp_count
FROM employees
WHERE salary > 50000 -- здесь нельзя использовать emp_count
GROUP BY department
HAVING COUNT(*) > 3 -- здесь можно
ORDER BY emp_count DESC; -- здесь можно