Урок 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, но запросом:

SQL
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 мы искали дубли сортировкой. В базе это делает один запрос:

SQL
SELECT order_id, COUNT(*) AS copies
FROM orders
GROUP BY order_id
HAVING COUNT(*) > 1;

HAVING отбирает уже готовые группы — здесь те, где строк больше одной. Если запрос вернул пустой результат, дублей нет. Подробно об этом — в уроке «Группировка: GROUP BY и HAVING».

Реклама по дням

Теперь посмотрим заказы из рекламы в мае по дням:

SQL
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:

SQL
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).

Решение
SQL
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 вопроса

Проверьте себя

Выберите вариант, который кажется вам правильным. В конце вы увидите свой результат и объяснение к каждому вопросу.

  1. Какой запрос посчитает число заказов по каждому источнику?

  2. Зачем в запросе условие WHERE order_date >= '2026-05-01' AND order_date < '2026-06-01'?

  3. Что найдёт запрос SELECT order_id, COUNT(*) FROM orders GROUP BY order_id HAVING COUNT(*) > 1;?

  4. Зачем аналитику JOIN?