SQL Studio / Разборы SQL / SQL для аналитика: запрос — ещё не вывод
Начинайте с вопроса бизнеса
«Сколько у нас покупателей?» — неполный вопрос. Покупатель за всё время, за месяц, с оплаченным заказом или с доставленным заказом? Пока это не определено, даже безошибочный SQL может дать неверный ответ.
Определите единицу подсчёта
Для расчёта конверсии клиентов в покупателей числитель и знаменатель должны относиться к одной аудитории и периоду наблюдения. Один клиент может сделать несколько заказов, но для конверсии он обычно учитывается один раз.
WITH buyers AS (
SELECT DISTINCT customer_id
FROM orders
WHERE status = 'delivered'
)
SELECT
COUNT(*) AS customers_n,
COUNT(b.customer_id) AS buyers_n,
ROUND(100.0 * COUNT(b.customer_id) /
NULLIF(COUNT(*), 0), 2) AS conversion_pct
FROM customers AS c
LEFT JOIN buyers AS b
ON b.customer_id = c.customer_id;
На нашей базе результат: 4 зарегистрированных клиента, 3 покупателя с доставкой, 75%. Это доля зарегистрированных клиентов с доставкой за доступную историю, а не конверсия сессий или рекламного канала.
Проверяйте деньги и связи таблиц
В аналитике интернет-магазина различайте созданные заказы, оплаченные суммы, возвраты и чистые продажи. При соединении заказов с позициями важно проверять уровень детализации, иначе отчёт может завысить суммы.
SQLite в тренажёре и MySQL 8 в отдельном курсе
SQL Studio исполняет бесплатные задания на SQLite в вашем браузере. Отдельный авторский курс Stepik использует MySQL 8 и заканчивается аналитическим отчётом. Знания о ключах JOIN, NULL, агрегациях и гранулярности переносятся, но синтаксис не всегда переносим буквально.
| Задача | SQLite (SQL Studio) | MySQL 8 (курс) |
|---|---|---|
| Прибавить семь дней | date(created_at, '+7 day') | DATE_ADD(created_at, INTERVAL 7 DAY) |
| Месяц в YYYY-MM | strftime('%Y-%m', created_at) | DATE_FORMAT(created_at, '%Y-%m') |
| Строки склеить | first_name || ' ' || last_name | CONCAT(first_name, ' ', last_name) |
| Деньги | INTEGER в копейках | DECIMAL(p,s) с явно заданной точностью или целые копейки |
| Нумерация по группе | ROW_NUMBER() OVER (...) | ROW_NUMBER() OVER (...) |
SQLite имеет динамические правила типов и преобразований, MySQL 8 — иные типы и настройки SQL mode. Для дат в MySQL DATETIME и TIMESTAMP различаются по поведению часового пояса. В обоих движках оконный ORDER BY не гарантирует порядок конечных строк без внешнего ORDER BY. Перед переносом отчёта сверяйте результаты, NULL, округление, индексы и временную зону на настоящем движке.
Что можно написать в выводе
«Трое из четырёх зарегистрированных клиентов имеют доставленный заказ — 75%». Нельзя писать «75% посетителей конвертировались благодаря рекламе»: у нас нет ни сессионного знаменателя, ни доказательств влияния рекламного канала.