SQL

БЛОК 2. Чтение данных — 9. Агрегатные функции

📚 22 вопросовПройти тест →
Лекция

БЛОК 2. Чтение данных — 9. Агрегатные функции

SQL

9. Агрегатные функции

🧭 Введение: как получить смысл из тысяч строк

Когда таблица растёт, почти всегда нужен не список строк, а итог:
сколько клиентов, какая сумма продаж, средний чек, минимальная и максимальная дата.
Именно это делают агрегатные функции SQL:
COUNT, SUM, AVG, MIN, MAX.
Главная ловушка темы — NULL.
Если не понимать, как агрегаты его обрабатывают, отчёт формально выполнится, но цифры будут неверными.
💡 Совет: Перед любым агрегатом сразу задавайте себе вопрос: «как в этом поле учитываются NULL
Вывод: Агрегаты превращают «сырые строки» в управленческие метрики, но требуют аккуратной работы с NULL.

⚠️ Проблема -> решение

Типичный старт новичка:
написать COUNT(column), думая, что это «количество строк», или AVG(column), не учитывая пропуски.
Проблемы:
  1. путают COUNT(*) и COUNT(column),
  2. забывают, что SUM/AVG игнорируют NULL,
  3. получают «странные» итоги без явной проверки входных данных.
Решение:
  1. выбирать агрегат под бизнес-вопрос,
  2. явно понимать семантику NULL,
  3. для аналитики строить простые запросы с 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 и аккуратные агрегаты.
  • Точный отчёт — это не только синтаксис, но и корректная интерпретация данных.
🎯

Проверьте знания

Закрепите материал — пройдите тест по теме «БЛОК 2. Чтение данных — 9. Агрегатные функции»

Пройти тест →