9. Агрегатные функции
🧭 Введение: как получить смысл из тысяч строк
Когда таблица растёт, почти всегда нужен не список строк, а итог:
сколько клиентов, какая сумма продаж, средний чек, минимальная и максимальная дата.
сколько клиентов, какая сумма продаж, средний чек, минимальная и максимальная дата.
Именно это делают агрегатные функции SQL:
COUNT, SUM, AVG, MIN, MAX.Главная ловушка темы —
Если не понимать, как агрегаты его обрабатывают, отчёт формально выполнится, но цифры будут неверными.
NULL.Если не понимать, как агрегаты его обрабатывают, отчёт формально выполнится, но цифры будут неверными.
💡 Совет:
Перед любым агрегатом сразу задавайте себе вопрос: «как в этом поле учитываются
NULL?»✅ Вывод:
Агрегаты превращают «сырые строки» в управленческие метрики, но требуют аккуратной работы с
NULL.⚠️ Проблема -> решение
Типичный старт новичка:
написать
написать
COUNT(column), думая, что это «количество строк», или AVG(column), не учитывая пропуски.Проблемы:
- путают
COUNT(*)иCOUNT(column), - забывают, что
SUM/AVGигнорируютNULL, - получают «странные» итоги без явной проверки входных данных.
Решение:
- выбирать агрегат под бизнес-вопрос,
- явно понимать семантику
NULL, - для аналитики строить простые запросы с
GROUP BYи понятными алиасами.
🟢 Если совсем просто:
Сначала формулируем метрику словами, потом выбираем правильный агрегат.
🎯 Как понять, что этап прошёл успешно:
Вы можете объяснить, почему число в отчёте именно такое, включая влияние
NULL.🛠️ Чем помогает и как работает
Агрегаты — это база любой отчётности: от админки до BI-дашбордов.
Они сжимают большой набор данных в краткую и полезную сводку.
Они сжимают большой набор данных в краткую и полезную сводку.
🟢 Если совсем просто:
Вместо 100000 строк получаем 1-10 чисел, по которым принимают решения.
🎯 Как понять, что этап прошёл успешно:
Вы уверенно строите запросы «сколько», «сумма», «среднее», «минимум», «максимум».
Чем помогает:
- считает количество сущностей и событий;
- даёт суммарные значения и средние;
- показывает границы диапазона (
MIN/MAX); - строит простую аналитику по группам (
GROUP BY).
Как это работает:
- Шаг 1: определяем объект анализа (таблица/фильтр).
- Шаг 2: выбираем агрегат под вопрос.
- Шаг 3: при необходимости группируем (
GROUP BY). - Шаг 4: проверяем влияние
NULL. - Шаг 5: подписываем метрики алиасами для читаемого результата.
✅ Вывод:
Хороший агрегатный запрос — это не просто функция, а чёткая бизнес-логика метрики.
📚 Ключевые термины (простыми словами)
Перед практикой синхронизируем словарь.
🟢 Если совсем просто:
Эти термины описывают, как SQL считает итоговые числа.
🎯 Как понять, что этап прошёл успешно:
Вы различаете поведение
COUNT(*) и COUNT(column) без подсказок.- Aggregate function (агрегатная функция) — функция, которая считает итог по набору строк.
- COUNT(*) — количество всех строк в выборке.
- COUNT(column) — количество строк, где значение колонки не
NULL. - COUNT(DISTINCT column) — количество уникальных не-NULL значений.
- SUM(column) — сумма не-NULL значений колонки.
- AVG(column) — среднее по не-NULL значениям.
- MIN(column) — минимальное не-NULL значение.
- MAX(column) — максимальное не-NULL значение.
- GROUP BY — разбиение данных на группы перед расчётом агрегатов.
- Metric (метрика) — числовой показатель, который нужен бизнесу.
✅ Вывод:
Понимание разницы между агрегатами напрямую влияет на корректность отчётов.
🔢 1. COUNT(*) vs COUNT(column)
COUNT кажется простой функцией, но именно здесь чаще всего делают логические ошибки.Ключевая разница:
COUNT(*) считает строки, а COUNT(column) — только непустые значения в колонке.🟢 Если совсем просто:
COUNT(*) = «сколько строк», COUNT(column) = «сколько заполненных значений».🎯 Как понять, что этап прошёл успешно:
Вы правильно выбираете вариант
COUNT в зависимости от бизнес-вопроса.Назначение:
Считать количество записей или количество заполненных значений.
Простыми словами:
Одна и та же таблица может дать разные числа для
COUNT(*) и COUNT(email).Для новичка:
Если нужен «объём выборки», почти всегда берите
COUNT(*).Аналогия:
Это как анкеты: всего анкет 100 (
COUNT(*)), а заполненных телефонов 73 (COUNT(phone)).Пример:
SELECT COUNT(*) AS total_rows, COUNT(phone) AS filled_phone_countFROM customers;Пример с уникальными значениями:
SELECT COUNT(DISTINCT user_id) AS unique_buyersFROM ordersWHERE status = 'paid';🔎 Как это происходит на практике:
- Контекст: CRM-команда оценивает качество заполнения профилей.
- Действия: сравнивает общее число клиентов с числом заполненных телефонов.
- Результат: виден разрыв между «есть запись» и «данные заполнены».
Характеристики:
COUNT(*)учитывает каждую строку;COUNT(column)игнорируетNULL;COUNT(DISTINCT column)считает только уникальные не-NULL значения;DISTINCTобычно дороже по вычислениям, но часто это единственно корректная метрика;- разница между ними — полезный сигнал качества данных.
Когда использовать:
COUNT(*) — для общего количества, COUNT(column) — для полноты конкретного поля.✅ Вывод:
COUNT(*) и COUNT(column) отвечают на разные вопросы, их нельзя считать взаимозаменяемыми.➕ 2. SUM / AVG / MIN / MAX
Эти агрегаты дают базовые числовые метрики: сумму, среднее, минимум и максимум.
Они работают по одной логике: считают только значения, которые не
Они работают по одной логике: считают только значения, которые не
NULL.🟢 Если совсем просто:
SUM — сколько всего, AVG — среднее, MIN/MAX — границы диапазона.🎯 Как понять, что этап прошёл успешно:
Вы можете за один запрос вернуть ключевые показатели периода.
Назначение:
Считать количественные итоги и диапазоны значений.
Простыми словами:
Это быстрый способ получить «картину сверху» по данным.
Для новичка:
Всегда подписывайте агрегаты алиасами (
AS total_revenue), иначе отчёт плохо читается.Аналогия:
Сводка по магазину за день: общая выручка, средний чек, самый дешёвый и самый дорогой заказ.
Пример:
SELECT SUM(total_amount) AS total_revenue, AVG(total_amount) AS avg_order_amount, MIN(total_amount) AS min_order_amount, MAX(total_amount) AS max_order_amountFROM ordersWHERE status = 'paid';🔎 Как это происходит на практике:
- Контекст: команда проверяет финансовые KPI за период.
- Действия: одним запросом получает 4 метрики.
- Результат: быстрый срез для дашборда или отчёта.
Характеристики:
SUMиAVGприменяют в основном к числовым полям;MIN/MAXработают и для дат/строк (по правилам сравнения СУБД);- если после
WHEREнет строк,COUNT(*)вернёт0, аSUM/AVG/MIN/MAXвернутNULL; - алиасы делают результат понятным без дополнительной обработки.
Когда использовать:
Для итогов по продажам, времени, длительности, количеству и другим числовым метрикам.
✅ Вывод:
SUM/AVG/MIN/MAX — минимальный набор для «первой аналитики» по таблице.🕳️ 3. Поведение агрегатов с NULL
NULL влияет на результат агрегатов сильнее, чем кажется.Важно понимать: большинство агрегатов игнорируют
NULL, но это не всегда совпадает с бизнес-ожиданием.🟢 Если совсем просто:
NULL не считается в COUNT(column), SUM, AVG, MIN, MAX.🎯 Как понять, что этап прошёл успешно:
Вы можете предсказать результат агрегата на данных с пропусками.
Назначение:
Избежать тихих ошибок в отчётах из-за пропущенных значений.
Простыми словами:
Если часть значений неизвестна (
NULL), агрегат считает только известные.Для новичка:
Не «лечите»
NULL через COALESCE(..., 0) автоматически — сначала проверьте, соответствует ли это бизнес-правилу.Аналогия:
Если у части сотрудников не указан возраст, средний возраст считается только по тем, где возраст известен.
Пример:
SELECT COUNT(*) AS rows_total, COUNT(discount_percent) AS rows_with_discount, AVG(discount_percent) AS avg_discount_percentFROM orders;Кейс
Подмена
NULL vs 0 (важное различие):
NULL = «неизвестно/не применимо», а 0 = «точно ноль».Подмена
NULL на 0 без бизнес-основания меняет смысл AVG/SUM.SELECT AVG(commission_percent) AS avg_commission_raw, AVG(COALESCE(commission_percent, 0)) AS avg_commission_with_zeroFROM partner_sales;🔎 Как это происходит на практике:
- Контекст: аналитик оценивает среднюю скидку.
- Действия: сравнивает общее число заказов и число строк со скидкой.
- Результат: видно, насколько репрезентативно среднее значение.
Характеристики:
AVG(column)делит сумму на число не-NULL значений;COUNT(*)всегда полезен как контрольный baseline;- разница между
COUNT(*)иCOUNT(column)показывает полноту данных.
Когда использовать:
Всегда при работе с nullable-колонками в отчётах.
✅ Вывод:
Без явной проверки
NULL агрегаты легко дают «красивые, но неверные» выводы.📊 4. Простые аналитические запросы
Простая аналитика обычно строится как: фильтр +
Это позволяет быстро получить метрики по категориям, статусам, датам.
GROUP BY + агрегаты.Это позволяет быстро получить метрики по категориям, статусам, датам.
🟢 Если совсем просто:
Разбиваем данные на группы и считаем метрики по каждой группе.
🎯 Как понять, что этап прошёл успешно:
Вы получаете сводную таблицу вида «группа -> количество/сумма/среднее».
Назначение:
Построить краткую аналитическую картину без сложных инструментов BI.
Простыми словами:
SQL сам собирает мини-отчёт прямо в запросе.
Для новичка:
В
SELECT с GROUP BY оставляйте только агрегаты и поля группировки.Аналогия:
Счёт по отделам: для каждого отдела — число сотрудников и средняя зарплата.
Пример:
SELECT status, COUNT(*) AS orders_count, SUM(total_amount) AS status_revenue, AVG(total_amount) AS avg_status_amountFROM ordersWHERE created_at >= DATE '2026-03-01' AND created_at < DATE '2026-04-01'GROUP BY statusORDER BY status_revenue DESC;🔎 Как это происходит на практике:
- Контекст: менеджер хочет понять структуру выручки по статусам.
- Действия: агрегируем за месяц по
status. - Результат: видна доля каждого статуса в общих показателях.
Характеристики:
WHEREрежет входной набор строк до агрегации;GROUP BYсоздаёт отдельный расчёт для каждой группы;- агрегаты дают итог внутри группы;
- сортировка итогов помогает быстро читать отчёт.
Когда использовать:
Для дашбордов и регулярных отчётов «по категориям/статусам/периодам».
✅ Вывод:
GROUP BY + агрегаты — первый рабочий уровень аналитики в SQL.🧩 5. Условные агрегаты (простая прод-аналитика)
Часто нужно посчитать несколько метрик в одном запросе:
например, сколько заказов оплачено и сколько отменено.
например, сколько заказов оплачено и сколько отменено.
🟢 Если совсем просто:
CASE внутри агрегата позволяет считать разные подмножества сразу.🎯 Как понять, что этап прошёл успешно:
Вы получаете несколько бизнес-метрик одним запросом без повторных сканов таблицы.
Назначение:
Собирать компактный отчёт по нескольким условиям одновременно.
Простыми словами:
Мы считаем «счётчики по правилам» в одном
SELECT.Для новичка:
Условный
COUNT часто пишут как SUM(CASE WHEN ... THEN 1 ELSE 0 END).Аналогия:
Табло на складе: отдельно «принято», «отгружено», «возврат».
Пример:
SELECT COUNT(*) AS total_orders, SUM(CASE WHEN status = 'paid' THEN 1 ELSE 0 END) AS paid_orders, SUM(CASE WHEN status = 'cancelled' THEN 1 ELSE 0 END) AS cancelled_orders, SUM(CASE WHEN status = 'paid' THEN total_amount ELSE 0 END) AS paid_revenueFROM ordersWHERE created_at >= DATE '2026-03-01' AND created_at < DATE '2026-04-01';Альтернатива для PostgreSQL через
FILTER:SELECT COUNT(*) AS total_orders, COUNT(*) FILTER (WHERE status = 'paid') AS paid_orders, SUM(total_amount) FILTER (WHERE status = 'paid') AS paid_revenueFROM ordersWHERE created_at >= DATE '2026-03-01' AND created_at < DATE '2026-04-01';🔎 Как это происходит на практике:
- Контекст: daily-отчёт по заказам.
- Действия: в одном запросе собирают счётчики и выручку по нужным статусам.
- Результат: backend отдаёт готовую сводку в API без дополнительной обработки.
Характеристики:
- экономит количество отдельных запросов;
- удобно для KPI-панелей;
- в PostgreSQL
FILTERчасто делает условные агрегаты короче и читабельнее; - требует аккуратной формулировки условий.
Когда использовать:
Когда нужно 2-5 связанных метрик из одной таблицы за один проход.
✅ Вывод:
Условные агрегаты — быстрый путь от «просто SQL» к реальной аналитике продукта.
⚖️ Сравнение агрегатов
| Функция | Что считает | Как ведёт себя с NULL |
|---|---|---|
COUNT(*) | Все строки | Учитывает каждую строку |
COUNT(column) | Непустые значения колонки | Игнорирует NULL |
SUM(column) | Сумму значений | Игнорирует NULL |
AVG(column) | Среднее значение | Игнорирует NULL в числителе и знаменателе |
MIN(column) | Минимум | Игнорирует NULL |
MAX(column) | Максимум | Игнорирует NULL |
✅ Вывод:
COUNT(*) — контроль общего объёма, остальные агрегаты работают по не-NULL значениям.📌 Must-know факты
COUNT(*)иCOUNT(column)почти всегда дают разные числа при nullable-данных.COUNT(DISTINCT ...)нужен для метрик уникальности (пользователи, заказы, товары).AVG(column)считает среднее только по заполненным значениям.SUM/AVG/MIN/MAXобычно игнорируютNULL.- При пустой выборке
COUNT(*) = 0, аSUM/AVG/MIN/MAX = NULL. - Перед агрегатами полезно проверить
COUNT(*)как baseline. GROUP BY— база простых аналитических запросов.- Алиасы метрик (
AS ...) обязательны для читаемых отчётов.
🧨 Частые мифы
❌ Миф:
COUNT(column) считает все строки таблицы.✅ Как правильно:
COUNT(column) считает только строки, где в этой колонке нет NULL.📎 Почему это важно:
Иначе отчёт по «количеству записей» будет занижен.
❌ Миф:
AVG делит на общее число строк.✅ Как правильно:
AVG делит на число не-NULL значений в колонке.📎 Почему это важно:
Среднее может сильно отличаться от интуитивного ожидания.
❌ Миф:
Если в данных есть
NULL, агрегаты «сломаются».✅ Как правильно:
Обычно агрегаты просто игнорируют
NULL, и запрос выполняется корректно.📎 Почему это важно:
Проблема не в падении запроса, а в возможной неверной интерпретации цифр.
❌ Миф:
Для простой аналитики нужен только BI-инструмент, SQL недостаточно.
✅ Как правильно:
GROUP BY + агрегаты покрывают большой класс базовых аналитических задач.📎 Почему это важно:
Можно быстро строить полезные отчёты прямо в БД.
❓ Часто спрашивают на собеседованиях
❓ Вопрос: В чём разница между
COUNT(*) и COUNT(column)?
✅ Ответ: COUNT(*) считает все строки, а COUNT(column) — только строки с не-NULL значением в колонке.❓ Вопрос: Как
AVG работает с NULL?
✅ Ответ: AVG игнорирует NULL и считает среднее только по заполненным значениям.❓ Вопрос: Что делают
MIN и MAX в агрегатах?
✅ Ответ: Возвращают минимальное и максимальное не-NULL значение в выборке/группе.❓ Вопрос: Что вернут агрегаты, если после
WHERE нет строк?
✅ Ответ: COUNT(*) вернёт 0, а SUM/AVG/MIN/MAX вернут NULL.❓ Вопрос: Зачем нужен
GROUP BY?
✅ Ответ: Чтобы считать отдельные агрегаты по каждой группе значений (например, по статусу или категории).❓ Вопрос: Как посчитать несколько метрик в одном запросе?
✅ Ответ: Использовать условные агрегаты через
SUM(CASE WHEN ... THEN ... END).❓ Вопрос: Как быстро проверить качество заполнения поля?
✅ Ответ: Сравнить
COUNT(*) и COUNT(column) для нужной выборки.🚫 Типичные ошибки
Ошибка 1: подменять COUNT(*) на COUNT(column)
❌ Неправильно:
Использовать
COUNT(email) как «общее число пользователей».✅ Правильно:
Для общего количества использовать
COUNT(*).Почему:
COUNT(email) не учитывает строки с NULL и занижает итог.Ошибка 2: не проверять влияние NULL на AVG
❌ Неправильно:
Считать, что
AVG(discount_percent) учитывает всех клиентов.✅ Правильно:
Проверять
COUNT(discount_percent) рядом с COUNT(*).Почему:
Среднее может быть посчитано по малой части данных.
Ошибка 3: использовать GROUP BY, но выводить лишние неагрегированные поля
❌ Неправильно:
Добавлять в
SELECT поля, которых нет в GROUP BY и не обёрнуты в агрегат.✅ Правильно:
В
SELECT оставлять только поля группировки и агрегаты.Почему:
Иначе запрос либо не выполнится, либо логика будет некорректной.
Ошибка 4: не подписывать метрики алиасами
❌ Неправильно:
Возвращать столбцы вида
sum, avg без понятных названий.✅ Правильно:
Использовать
AS total_revenue, AS avg_order_amount и т.д.Почему:
API и отчёты становятся читаемыми и поддерживаемыми.
Ошибка 5: считать каждую метрику отдельным запросом без необходимости
❌ Неправильно:
Писать 4-5 отдельных
SELECT для связанных KPI.✅ Правильно:
Комбинировать агрегаты и условные агрегаты в одном запросе.
Почему:
Это проще сопровождать и часто быстрее выполняется.
✅ Best Practices
- Для базового контроля всегда смотрите пару
COUNT(*)иCOUNT(column). - Для метрик уникальности явно используйте
COUNT(DISTINCT ...). - Подписывайте каждую метрику осмысленным алиасом.
- Явно проверяйте влияние
NULLперед публикацией отчёта. - Для простой аналитики начинайте с
GROUP BYпо ключевому измерению. - Сложные KPI собирайте условными агрегатами в одном запросе.
- Перед продом проверяйте метрики на маленькой тестовой выборке вручную.
🧾 Заключение
Агрегатные функции — это фундамент аналитики в SQL.
Они позволяют быстро перейти от «сырых данных» к метрикам, которыми управляют продуктом и бизнесом.
Они позволяют быстро перейти от «сырых данных» к метрикам, которыми управляют продуктом и бизнесом.
Ключевые мысли
COUNT(*)иCOUNT(column)отвечают на разные вопросы.SUM/AVG/MIN/MAXнужно читать через призмуNULL.- Простая аналитика строится через
GROUP BYи аккуратные агрегаты. - Точный отчёт — это не только синтаксис, но и корректная интерпретация данных.