Когда вы выгружаете данные из базы интернет-магазина, то вывести список всех заказов — это только половина дела.
Настоящая аналитика начинается там, где нужно узнать общую выручку за месяц, средний чек или количество активных клиентов. Чтобы не вытаскивать весь массив данных в код своего приложения и не писать циклы для подсчёта итогов вручную, эту работу делегируют самой базе данных.
ВАМ ПРИШЛО ПРИГЛАШЕНИЕ 💌
Приходите к нам в соцсети поделиться своим мнением и почитать, что пишут другие. А ещё там выходит дополнительный контент, которого нет на сайте — шпаргалки, опросы и разная дурка. В общем, вот тележка, вот ВК — велком!
Что такое агрегатные функции в SQL
Агрегатные функции в SQL — это команды, которые берут целый столбец или группу строк и сворачивают их в одно итоговое значение. Вы отдаёте движку базы данных тысячу записей, а он возвращает вам одну цифру.
На практике такие агрегирующие функции SQL экономят колоссальное количество ресурсов сети и оперативной памяти: вы отправляете правильный запрос и получаете готовый математический результат. Дальше разберём основные агрегатные функции SQL, которые закрывают большинство рабочих задач при проектировании отчётов и аналитике.
COUNT: подсчёт строк
Оператор подсчёта — самый популярный инструмент в аналитике. Здесь легко допустить ошибку, поскольку функция ведёт себя по-разному в зависимости от того, что конкретно вы ей передадите. Есть три варианта использования COUNT:
COUNT(*)считает абсолютно все строки в таблице, включая те, где данные отсутствуют (значения NULL). Это самый надёжный способ узнать физическое количество записей в базе.COUNT(column_name)считает только те строки, где в конкретном столбце есть реальные данные. Строки со значениемNULLполностью игнорируются.COUNT(DISTINCT column_name)— такой синтаксис нужен для подсчёта уникальных значений. Если десять пользователей укажут в анкете город «Москва», база посчитает это как единицу.
Допустим, нам нужно узнать:
- Сколько всего клиентов в базе.
- Сколько клиентов указали страну.
- Сколько уникальных стран представлено.
Пишем три запроса:
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;

Логика выполнения строится так:
FROMберет таблицуOrders.WHEREсначала отсекает все заказы, где сумма меньше или равна 200 — то есть убирает мелкие покупки ещё до группировки.GROUP BYсобирает оставшиеся заказы поcustomer_id, формируя группы покупок каждого клиента.SUMсчитает общую сумму заказов внутри каждой группы.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 и при этом немного сэкономить, держите наш промокод на курсы Практикума. Он даст вам скидку при оплате, поможет с льготной ипотекой или безлимитом на маркетплейсах. Ладно, окей, это просто скидка, без остального, но хорошая.


