← Назад к вопросам

Какие знаешь агрегатные функции?

1.8 Middle🔥 201 комментариев
#Базы данных и SQL

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Агрегатные функции в SQL: Практическое применение для Business Analyst

Агрегатные функции (aggregate functions) — это мощный инструмент для анализа данных. За 10+ лет я использовал их почти каждый день при работе с аналитикой, метриками и отчётами. Расскажу о самых важных и как я их применяю на практике.

Что такое агрегатные функции?

Агрегатные функции — это SQL функции, которые берут несколько строк и возвращают одно значение (агрегат).

Основные агрегатные функции:

  • COUNT() — посчитать количество
  • SUM() — сумма
  • AVG() — среднее значение
  • MIN() — минимум
  • MAX() — максимум
  • GROUP_CONCAT() — объединить в строку

1. COUNT() — Подсчёт количества

Базовый пример:

SELECT COUNT(*) as total_users
FROM users;

Результат: 50000 (всего пользователей)

С условием:

SELECT COUNT(*) as active_users
FROM users
WHERE last_login > DATE_SUB(NOW(), INTERVAL 30 DAY);

Результат: 15000 (активные за последние 30 дней)

С GROUP BY (считаем по категориям):

SELECT 
  country,
  COUNT(*) as user_count
FROM users
GROUP BY country
ORDER BY user_count DESC;

Результат:
USA       | 20000
Europe    | 15000
Asia      | 10000
Other     | 5000

В контексте бизнеса:

  • "Сколько пользователей в каждой стране?" → COUNT() с GROUP BY
  • "Сколько заказов сделал каждый клиент?" → COUNT() по клиентам

COUNT(DISTINCT)

Пример: Уникальные пользователи

SELECT COUNT(DISTINCT user_id) as unique_users
FROM orders;

Результат: 12000 (12000 пользователей сделали заказы)

Важно: COUNT(*) вернул бы 50000 (количество заказов), а COUNT(DISTINCT user_id) вернёт количество уникальных пользователей.

В контексте бизнеса:

  • DAU (Daily Active Users) → COUNT(DISTINCT user_id) за день
  • Repeat customers → COUNT(DISTINCT user_id) с количеством заказов > 1

2. SUM() — Сумма

Пример: Общая выручка

SELECT SUM(amount) as total_revenue
FROM orders;

Результат: 5000000 ($5M выручка)

По месяцам:

SELECT 
  DATE_TRUNC('month', created_at) as month,
  SUM(amount) as monthly_revenue
FROM orders
GROUP BY DATE_TRUNC('month', created_at)
ORDER BY month DESC;

Результат:
2024-03 | 500000
2024-02 | 480000
2024-01 | 450000

В контексте бизнеса:

  • MRR (Monthly Recurring Revenue) → SUM() по месячным подпискам
  • Total customer lifetime value → SUM() всех платежей user'а
  • Profit by segment → SUM(revenue) - SUM(costs) по сегментам

Conditional SUM

Пример: Разные суммы по типам

SELECT 
  SUM(CASE WHEN status = 'completed' THEN amount ELSE 0 END) as completed_revenue,
  SUM(CASE WHEN status = 'pending' THEN amount ELSE 0 END) as pending_revenue,
  SUM(CASE WHEN status = 'cancelled' THEN amount ELSE 0 END) as cancelled_revenue
FROM orders;

Результат:
completed_revenue | 4800000
pending_revenue   | 150000
cancelled_revenue | 50000

3. AVG() — Среднее значение

Пример: Средний размер заказа

SELECT AVG(amount) as avg_order_value
FROM orders;

Результат: 100 (средний заказ $100)

По сегментам:

SELECT 
  customer_segment,
  AVG(amount) as avg_order_value,
  COUNT(*) as order_count
FROM orders
GROUP BY customer_segment;

Результат:
Premium     | 500   | 8000
Standard    | 100   | 40000
Free        | 20    | 10000

В контексте бизнеса:

  • AOV (Average Order Value) — key метрик для e-commerce
  • ARPU (Average Revenue Per User) — ARPU = Total Revenue / Total Users
  • Average handling time в support

4. MIN() и MAX() — Минимум и максимум

Пример: Диапазон цен

SELECT 
  MIN(price) as lowest_price,
  MAX(price) as highest_price,
  MAX(price) - MIN(price) as price_range
FROM products;

Результат:
lowest_price  | 5
highest_price | 1000
price_range   | 995

По датам: Первый и последний заказ пользователя

SELECT 
  user_id,
  MIN(created_at) as first_order_date,
  MAX(created_at) as last_order_date,
  DATEDIFF(day, MIN(created_at), MAX(created_at)) as days_between
FROM orders
GROUP BY user_id;

В контексте бизнеса:

  • Самый дорогой товар, самый дешевый товар
  • Дата первого использования юзера
  • Дата последнего платежа (для выявления churn)

5. GROUP_CONCAT() или STRING_AGG() — Объединение строк

Пример: Список товаров в заказе

SELECT 
  order_id,
  GROUP_CONCAT(product_name, ', ') as products
FROM order_items
GROUP BY order_id;

Результат:
order_123 | "Laptop, Mouse, Keyboard, Monitor"
order_124 | "Phone, Case, Screen Protector"

В контексте бизнеса:

  • Быстрый preview что в заказе
  • Список активных фич пользователя

Комбинирование агрегатов: Реальный пример

Задача: Анализ продаж по месяцам и сегментам

SELECT 
  DATE_TRUNC('month', o.created_at) as month,
  u.segment,
  COUNT(DISTINCT o.id) as order_count,
  COUNT(DISTINCT o.user_id) as unique_customers,
  SUM(o.amount) as total_revenue,
  AVG(o.amount) as avg_order_value,
  MIN(o.amount) as min_order,
  MAX(o.amount) as max_order
FROM orders o
JOIN users u ON o.user_id = u.id
WHERE o.created_at >= '2024-01-01'
GROUP BY DATE_TRUNC('month', o.created_at), u.segment
ORDER BY month DESC, total_revenue DESC;

Результат:
month   | segment  | orders | customers | revenue | avg_order | min | max
────────┼──────────┼────────┼───────────┼─────────┼───────────┼─────┼─────
2024-03 | Premium  | 8000   | 1500      | 4000000 | 500       | 100 | 5000
2024-03 | Standard | 40000  | 8000      | 4000000 | 100       | 10  | 1000
2024-03 | Free     | 10000  | 5000      | 200000  | 20        | 5   | 100
2024-02 | Premium  | 7500   | 1400      | 3750000 | 500       | 100 | 5000
...

Эта одна query вернула полный анализ! Я вижу:

  • Какой месяц лучший (март лучше февраля)
  • Какой сегмент самый прибыльный (Premium)
  • AOV для каждого сегмента
  • Trend по месяцам

HAVING — Фильтрация агрегатов

Проблема: Я хочу видеть только сегменты с revenue > $1M

SELECT 
  customer_segment,
  SUM(amount) as total_revenue
FROM orders
GROUP BY customer_segment
HAVING SUM(amount) > 1000000;  -- только > $1M
ORDER BY total_revenue DESC;

Результат:
customer_segment | total_revenue
─────────────────┼──────────────
Premium          | 4000000
Standard         | 2500000

Разница:

  • WHERE фильтрует строки ДО агрегации
  • HAVING фильтрует ДО результаты ПОСЛЕ агрегации

В контексте бизнеса:

  • "Показать мне только города с более чем 1000 заказов в месяц"
  • "Показать только пользователей, которые потратили более $500"

Практические примеры из моей работы

Пример 1: Чёрный список пользователей

Часто нужно найти пользователей, которые совершают подозрительную активность.

SELECT 
  user_id,
  email,
  COUNT(*) as order_count,
  SUM(amount) as total_spent,
  COUNT(DISTINCT payment_method) as payment_methods_count,
  COUNT(CASE WHEN status = 'refunded' THEN 1 END) as refunds
FROM orders
GROUP BY user_id, email
HAVING 
  refunds > 3  -- более 3 возвратов
  AND payment_methods_count > 5  -- множество способов оплаты
ORDER BY refunds DESC;

Это выявляет потенциально мошеннические user'ов.

Пример 2: Cohort Analysis (Анализ когорт)

Сравниваем retention разных cohort'ов (групп пользователей, зарегистрировавшихся в одном месяце).

SELECT 
  DATE_TRUNC('month', u.created_at) as signup_month,
  DATE_TRUNC('month', o.created_at) as order_month,
  COUNT(DISTINCT o.user_id) as active_users
FROM orders o
JOIN users u ON o.user_id = u.id
WHERE o.created_at >= u.created_at  -- только orders после signup
GROUP BY DATE_TRUNC('month', u.created_at), DATE_TRUNC('month', o.created_at)
ORDER BY signup_month, order_month;

Результат: Таблица retention
signup | month1 | month2 | month3 | month4
────────┼────────┼────────┼────────┼────────
Jan    | 10000  | 6000   | 4200   | 3000
Feb    | 12000  | 7000   | 4800   | ...
Mar    | 15000  | 8500   | ...    | ...

Эта таблица показывает, как много пользователей из каждой cohort'ы остаются активны.

Пример 3: Чувствительность к цене (Price Sensitivity)

Проверяю, как цена влияет на конверсию.

SELECT 
  ROUND(price, 0) as price_point,
  COUNT(*) as impressions,
  COUNT(CASE WHEN purchased = 1 THEN 1 END) as purchases,
  ROUND(100.0 * COUNT(CASE WHEN purchased = 1 THEN 1 END) / COUNT(*), 2) as conversion_rate
FROM product_views
GROUP BY ROUND(price, 0)
HAVING COUNT(*) > 100  -- только если достаточно данных
ORDER BY price;

Результат:
price | impressions | purchases | conversion
──────┼─────────────┼───────────┼───────────
10    | 5000        | 800       | 16.00%
20    | 4800        | 720       | 15.00%
50    | 4000        | 400       | 10.00%
100   | 2000        | 120       | 6.00%

Видно, что цена 10 имеет лучшую конверсию.

Ошибки, которых я избегаю

Ошибка 1: Использование SUM без GROUP BY

-- Неправильно
SELECT user_id, SUM(amount)
FROM orders;
-- Ошибка: какой user_id выбрать?

-- Правильно
SELECT user_id, SUM(amount)
FROM orders
GROUP BY user_id;

**Ошибка 2: COUNT(*) когда нужен COUNT(DISTINCT)

-- Неправильно (считает строки, не пользователей)
SELECT COUNT(*) FROM orders;
-- Результат: 50000 (количество заказов)

-- Правильно
SELECT COUNT(DISTINCT user_id) FROM orders;
-- Результат: 12000 (количество уникальных пользователей)

Ошибка 3: Смешивание агрегатов с columns, которые не в GROUP BY

-- Неправильно в некоторых БД
SELECT user_id, email, SUM(amount)
FROM orders
GROUP BY user_id;
-- email не в GROUP BY, может быть ошибка

-- Правильно
SELECT user_id, SUM(amount)
FROM orders
GROUP BY user_id;
-- или если нужен email
SELECT o.user_id, u.email, SUM(o.amount)
FROM orders o
JOIN users u ON o.user_id = u.id
GROUP BY o.user_id, u.email;

Результат мастерства в агрегатных функциях

  • Быстрые insights из данных (одна query вместо множества)
  • Автоматизированные отчёты (query делает всю работу)
  • Глубокое понимание бизнеса (видишь паттерны, которые другие пропускают)
  • Быстрое обнаружение проблем (anomaly detection через агрегаты)

Агрегатные функции — это основа SQL анализа. Business Analyst без мастерства в них — это как водитель без умения ездить.

Какие знаешь агрегатные функции? | PrepBro