Агрегатные функции в SQL: COUNT, SUM, AVG, MIN и MAX

База для аналитики

Агрегатные функции в SQL: COUNT, SUM, AVG, MIN и MAX

Когда вы выгружаете данные из базы интернет-магазина, то вывести список всех заказов — это только половина дела.

Настоящая аналитика начинается там, где нужно узнать общую выручку за месяц, средний чек или количество активных клиентов. Чтобы не вытаскивать весь массив данных в код своего приложения и не писать циклы для подсчёта итогов вручную, эту работу делегируют самой базе данных.

ВАМ ПРИШЛО ПРИГЛАШЕНИЕ 💌
Приходите к нам в соцсети поделиться своим мнением и почитать, что пишут другие. А ещё там выходит дополнительный контент, которого нет на сайте — шпаргалки, опросы и разная дурка. В общем, вот тележка, вот ВК — велком!

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

Агрегатные функции в SQL — это команды, которые берут целый столбец или группу строк и сворачивают их в одно итоговое значение. Вы отдаёте движку базы данных тысячу записей, а он возвращает вам одну цифру.

На практике такие агрегирующие функции SQL экономят колоссальное количество ресурсов сети и оперативной памяти: вы отправляете правильный запрос и получаете готовый математический результат. Дальше разберём основные агрегатные функции SQL, которые закрывают большинство рабочих задач при проектировании отчётов и аналитике.

COUNT: подсчёт строк

Оператор подсчёта — самый популярный инструмент в аналитике. Здесь легко допустить ошибку, поскольку функция ведёт себя по-разному в зависимости от того, что конкретно вы ей передадите. Есть три варианта использования COUNT:

  • COUNT(*)считает абсолютно все строки в таблице, включая те, где данные отсутствуют (значения NULL). Это самый надёжный способ узнать физическое количество записей в базе.
  • COUNT(column_name) считает только те строки, где в конкретном столбце есть реальные данные. Строки со значением NULL полностью игнорируются.
  • COUNT(DISTINCT column_name) — такой синтаксис нужен для подсчёта уникальных значений. Если десять пользователей укажут в анкете город «Москва», база посчитает это как единицу.

Допустим, нам нужно узнать:

  1. Сколько всего клиентов в базе.
  2. Сколько клиентов указали страну.
  3. Сколько уникальных стран представлено.

Пишем три запроса:

SELECT 
    COUNT(*) AS total_customers,
    COUNT(country) AS customers_with_country,
    COUNT(DISTINCT country) AS unique_countries
FROM Customers;

SUM: сумма значений

Функция сложения работает только с числовыми столбцами. Базовый синтаксис выглядит так: SUM(price).

Здесь важно помнить о поведении системы при встрече с пустыми ячейками. Функция полностью игнорирует строки со значением NULL, а не считает их нулями. Если вы просто складываете общую выручку, это не критично. Но если вы используете математические выражения прямо внутри функции, например, складываете цену и налог, где налог может быть не указан, игнорирование пустых полей приведет к тому, что вся строка выпадет из итоговой суммы и результат исказится.

Например, посчитаем сумму всех заказов из таблицы Orders:

SELECT 
    SUM(amount)
FROM Orders;

AVG: среднее значение

Чтобы найти среднее значение в SQL, используют функцию AVG. Она складывает все значения в столбце и делит их на количество строк. И вот тут особенность игнорирования пустых ячеек может серьёзно сломать вашу статистику.

Допустим, в отделе пять сотрудников, но премия указана только у троих (остальные ячейки пустые, то есть NULL). Если мы напишем AVG(bonus), то функция AVG сложит три премии и разделит их на три. Получится красивая, но абсолютно неверная картина по отделу. Чтобы база честно посчитала среднее по всем пяти сотрудникам, пустые значения нужно принудительно превратить в нули. Для этого в SQL есть функция COALESCE: выражение AVG(COALESCE(bonus, 0)) заставит систему разделить общую сумму на пятерых.

MIN и MAX: минимальное и максимальное значение

Чтобы найти крайние точки в наборе данных, используют эти две симметричные функции. Важно знать, что минимальное и максимальное значение SQL умеет искать не только в столбцах с цифрами.

Если передать в функцию столбец с датами, MAX() вернёт самую позднюю дату (например, когда был сделан последний заказ), а MIN() — самую раннюю. Если отдать им текстовый столбец, база данных отсортирует строки по алфавиту. Для кириллицы MIN вернёт слово, начинающееся на букву «А», а MAX — слово, которое ближе всего к букве «Я».

Посмотрим на простой пример с таблицей Customers. Допустим, у нас есть список клиентов, и мы хотим узнать возраст самого молодого и самого старшего клиента в базе.

SELECT MIN(age) AS min_age,
       MAX(age) AS max_age
FROM Customers;

В нашем случае:

  • MIN(age) вернёт 22 (самые молодые клиенты — Robert и David).
  • MAX(age) вернёт 31 (самый старший клиент — John Doe).

GROUP BY: агрегатные функции по группам

До этого момента мы считали общие итоги по всей таблице целиком. Но реальные бизнес-задачи требуют большей детализации. Обычно вам нужно узнать не всю выручку магазина разом, а сумму покупок по каждому клиенту отдельно или средний чек по каждой категории товаров.

Для этого применяется группировка SQL с помощью оператора GROUP BY. Он берёт таблицу, разбивает её на смысловые блоки по какому-то признаку, и только потом база применяет агрегатную функцию к каждому блоку.

SELECT customer_id, SUM(amount)
FROM orders
GROUP BY customer_id;

В этом запросе база сначала соберет все заказы конкретного клиента (customer_id) в отдельную группу, а потом сложит суммы (amount) внутри каждой группы. На выходе мы получим список клиентов и потраченные ими суммы.

HAVING: фильтрация после группировки

Когда вы сгруппировали данные и посчитали суммы, вам часто нужно отфильтровать результат. Например, вывести только тех клиентов, чья общая сумма заказов превысила 100 000 рублей. Логичный порыв новичка — написать конструкцию WHERE SUM(price) > 100000. Но база выдаст синтаксическую ошибку.

Правило гласит: агрегатную функцию нельзя использовать внутри оператора WHERE. Он фильтрует исходные строки таблиц до того, как база начнёт их группировать и складывать.

Чтобы отфильтровать уже готовые, сгруппированные итоги, используют оператор HAVING. HAVING в SQL — это прямой аналог WHERE, но предназначенный исключительно для агрегированных данных, он применяется в самом конце. Дальше рассмотрим на примере, как это работает.

Пример: WHERE и HAVING в одном запросе

Допустим, у нас есть таблица заказов Orders, где хранится информация о покупках клиентов: сумма заказа, идентификатор клиента и другие поля. Наша задача: найти клиентов, которые сначала делают достаточно крупные заказы, а в сумме за все покупки набирают больше заданного порога.

Для этого используется типичный SQL-запрос с WHERE и HAVING — они решают разные задачи на разных этапах обработки данных.

Посмотрим на сам запрос и разберём порядок, в котором СУБД (система управления базами данных) выполняет его под капотом:

SELECT customer_id, SUM(amount)
FROM Orders
WHERE amount > 200
GROUP BY customer_id
HAVING SUM(amount) > 1000;

Логика выполнения строится так:

  1. FROM берет таблицу Orders.
  2. WHERE сначала отсекает все заказы, где сумма меньше или равна 200 — то есть убирает мелкие покупки ещё до группировки.
  3. GROUP BY собирает оставшиеся заказы по customer_id, формируя группы покупок каждого клиента.
  4. SUM считает общую сумму заказов внутри каждой группы.
  5. HAVING уже после агрегации отсекает клиентов, у которых итоговая сумма покупок меньше или равна 1000.

Полезный блок со скидкой

Порядок FROM → WHERE → GROUP BY → HAVING запоминается за минуту. Сложности начинаются на реальных данных: три таблицы вместо одной, дубли после соединения, запрос, который отрабатывает сорок секунд.

Это разбирают на практике: «SQL для работы с данными и аналитики» — со стороны аналитика, «SQL для разработки» — со стороны приложения.

Промокод: KOD (можно просто нажать) даст скидку при покупке любого курса.

Бесплатная вводная часть тоже есть — карту привязывать не нужно.

Частые ошибки при работе с агрегатными функциями

Разберём основные ошибки SQL, которые чаще всего могут возникнуть при проектировании сложных отчётов:

  • Агрегатная функция в WHERE. Как мы разобрали выше, условие WHERE COUNT(id) > 5 сломает запрос. Для фильтрации готовых групп используйте только HAVING.
  • Несгруппированные столбцы в SELECT. Если вы сгруппировали данные по client_id (клиенту), то не можете просто так вывести в ответ столбец order_date (дата заказа). У одного клиента может быть много заказов в разные дни, и база не поймёт, какую именно дату ей показывать в итоговой строке. Любой столбец в SELECT должен быть либо указан в GROUP BY, либо обёрнут в агрегатную функцию (например, MAX(order_date), чтобы вывести дату последнего заказа).
  • Путаница между COUNT(*) и COUNT(столбец). Вызов COUNT(phone_number) покажет вам количество пользователей, которые указали номер телефона в анкете. Чтобы узнать реальное количество зарегистрированных пользователей, включая тех, у кого телефон NULL, нужно всегда писать COUNT(*).

Советуем дополнительно почитать

Что такое MySQL и зачем он нужен — установка, первые команды и особенности самой распространённой базы, на которой чаще всего и выполняют такие запросы.

Tableau — сервис визуализации данных — куда попадают посчитанные агрегаты дальше: сборка дашбордов и графиков без написания кода.

Библиотека Pandas в Python и что с ней можно делать — те же группировки и агрегаты, но на стороне приложения; полезно понимать, когда считать в базе, а когда в коде.

SQL-инъекции: механика атак, типы и методы защиты с примерами — что происходит, когда пользовательский ввод попадает в запрос напрямую, и как этого избежать.

Кто такой Data Engineer и как им стать — профессия человека, который строит хранилища и пайплайны под такие запросы, с задачами и требованиями.

Бонус для читателей

Если вам интересно погрузиться в мир IT и при этом немного сэкономить, держите наш промокод на курсы Практикума. Он даст вам скидку при оплате, поможет с льготной ипотекой или безлимитом на маркетплейсах. Ладно, окей, это просто скидка, без остального, но хорошая.

Вам может быть интересно
easy
[anycomment]
Exit mobile version