Урок 5 из 8 · 25 мин · обновлено
SQL для аналитика: выборки, группировки, соединения
Зачем аналитику SQL, как запросом посчитать заказы по источникам и месяцам, найти дубли, посмотреть динамику по дням и соединить заказы с данными о покупателях.
Зачем аналитику SQL
Выгрузка заказов «Чайной лавки» за два месяца — 1 456 строк, с ней справится и таблица. Но у крупного магазина заказов миллионы, а ещё есть таблицы с посещениями, товарами и возвратами. В Excel такое не поместится, да и выгрузку каждый раз ждать от разработчиков неудобно.
Поэтому аналитики сами пишут запросы к базе данных на SQL. Запрос сразу считает ответ на стороне базы и возвращает несколько строк результата. Если вы ещё не знакомы с SQL, загляните в курс «SQL с нуля» — здесь мы разберём, как аналитик применяет его на деле.
В базе «Чайной лавки» нас интересуют две таблицы:
orders— заказы:order_id,order_date,customer_id,source,amount;customers— покупатели:customer_id,first_order_date(дата первой покупки),city.
Заказы по источникам и месяцам
Повторим расчёт сводной таблицы из урока 4, но запросом:
SELECT SUBSTR(order_date, 1, 7) AS month,
source,
COUNT(*) AS orders,
SUM(amount) AS revenue
FROM orders
WHERE order_date >= '2026-04-01' AND order_date < '2026-06-01'
GROUP BY month, source
ORDER BY source, month;
Разберём по частям:
SUBSTR(order_date, 1, 7)берёт первые 7 символов даты —2026-05— и превращает дату в месяц;WHEREоставляет заказы с 1 апреля по 31 мая;GROUP BY month, sourceсобирает заказы в группы «месяц + источник»;COUNT(*)иSUM(amount)считают число заказов и выручку в каждой группе.
Результат:
month | source | orders | revenue
2026-04 | поиск | 400 | 600000
2026-05 | поиск | 395 | 592500
2026-04 | рассылка | 150 | 225000
2026-05 | рассылка | 149 | 223500
2026-04 | реклама | 250 | 375000
2026-05 | реклама | 112 | 168000
Те же цифры, что в сводной таблице, плюс выручка. Источники идут по алфавиту — так их отсортировал ORDER BY source. Только запрос не нужно собирать мышкой заново: в июне его достаточно перезапустить с новыми датами.
Функции для работы с датами в разных базах называются по-разному. SUBSTR работает в SQLite — её использует онлайн-песочница из курса SQL. В PostgreSQL то же самое делают функцией date_trunc, а в MySQL — DATE_FORMAT.
Проверка на дубли
В уроке 3 мы искали дубли сортировкой. В базе это делает один запрос:
SELECT order_id, COUNT(*) AS copies
FROM orders
GROUP BY order_id
HAVING COUNT(*) > 1;
HAVING отбирает уже готовые группы — здесь те, где строк больше одной. Если запрос вернул пустой результат, дублей нет. Подробно об этом — в уроке «Группировка: GROUP BY и HAVING».
Реклама по дням
Теперь посмотрим заказы из рекламы в мае по дням:
SELECT order_date, COUNT(*) AS orders
FROM orders
WHERE source = 'реклама'
AND order_date >= '2026-05-01' AND order_date < '2026-06-01'
GROUP BY order_date
ORDER BY order_date;
Фрагмент результата:
order_date | orders
2026-05-09 | 8
2026-05-10 | 9
2026-05-11 | 7
2026-05-12 | 2
2026-05-13 | 1
2026-05-14 | 1
…
Резкий обрыв 12 мая — тот самый день, когда снизили дневной лимит на рекламу.
Соединяем таблицы: новые и постоянные покупатели
Есть вопрос важнее: кого именно мы потеряли — новых покупателей или постоянных? Если реклама в основном приводит новых, то её урезание ударит по росту магазина в будущем. Дата первой покупки лежит в другой таблице, customers. Соединим таблицы с помощью JOIN:
SELECT SUBSTR(o.order_date, 1, 7) AS month,
COUNT(*) AS new_customers
FROM orders AS o
JOIN customers AS c ON c.customer_id = o.customer_id
WHERE o.source = 'реклама'
AND o.order_date = c.first_order_date
AND o.order_date >= '2026-04-01' AND o.order_date < '2026-06-01'
GROUP BY month;
JOIN customers … ON c.customer_id = o.customer_idприклеивает к каждому заказу строку его покупателя;o.order_date = c.first_order_dateоставляет только первые покупки — то есть новых покупателей;AS oиAS c— короткие имена таблиц, чтобы не писать их целиком.
Результат:
month | new_customers
2026-04 | 180
2026-05 | 75
В апреле реклама привела 180 новых покупателей, в мае — 75. Экономия на рекламе стоила магазину не только майской выручки, но и 105 покупателей, которые могли бы вернуться за следующим заказом. Как считать, сколько людей возвращается, разберём в следующем уроке. А подробно о соединении таблиц — в уроке «Соединение таблиц: JOIN и LEFT JOIN».
Попробуйте сами. Напишите запрос, который посчитает средний чек по каждому источнику за май. Подсказка: средний чек — это AVG(amount).
Решение
SELECT source, AVG(amount) AS avg_check
FROM orders
WHERE order_date >= '2026-05-01' AND order_date < '2026-06-01'
GROUP BY source;
Результат для «Чайной лавки»:
source | avg_check
поиск | 1500.0
рассылка | 1500.0
реклама | 1500.0
Средний чек во всех источниках одинаковый — ещё одно подтверждение, что дело не в том, сколько люди тратят, а в том, сколько их пришло.
Мини-тест · 4 вопроса
Проверьте себя
Выберите вариант, который кажется вам правильным. В конце вы увидите свой результат и объяснение к каждому вопросу.
-
Какой запрос посчитает число заказов по каждому источнику?
GROUP BY source собирает заказы в группы по источнику, COUNT(*) считает строки в каждой группе.
-
Зачем в запросе условие WHERE order_date >= '2026-05-01' AND order_date < '2026-06-01'?
WHERE отбирает строки до группировки. Здесь остаются заказы с 1 мая включительно до 1 июня, не включая его, — то есть весь май.
-
Что найдёт запрос SELECT order_id, COUNT(*) FROM orders GROUP BY order_id HAVING COUNT(*) > 1;?
Группируем по номеру заказа и оставляем группы, где строк больше одной. Номер заказа должен быть уникальным, значит это дубли.
-
Зачем аналитику JOIN?
Данные почти всегда лежат в нескольких таблицах: заказы отдельно, покупатели отдельно. JOIN собирает их в одну выборку по customer_id.