SQL Studio / Разборы SQL / Как посчитать повторные покупки клиентов через SQL
Определение для примера: повторный покупатель — клиент с двумя или более доставленными заказами в имеющихся данных. Отменённые и находящиеся в обработке заказы не учитываем. Это учебная метрика, а не единственно возможное определение повторной покупки.
1. Найти покупателей с двумя и более доставками
В небольшой SQL Studio есть шесть заказов: четыре доставленных, один отменённый, один в обработке. Выполним подсчёт доставок по клиентам:
SELECT customer_id, COUNT(*) AS delivered_n
FROM orders
WHERE status = 'delivered'
GROUP BY customer_id
HAVING COUNT(*) >= 2
ORDER BY customer_id;
Результат — клиент №1, две доставки (заказы 201 и 203). Борис и Вера имеют по одной доставке; Данил не заказывал ничего.
2. Доля повторных покупателей: выбираем знаменатель
Если вопрос «сколько доставленных покупателей вернулось хотя бы один раз?», знаменатель — все клиенты, у которых есть хотя бы одна доставка. Для нашей базы это три человека, из них один повторный.
WITH delivered AS (
SELECT customer_id, COUNT(*) AS orders_n
FROM orders
WHERE status = 'delivered'
GROUP BY customer_id
)
SELECT COUNT(*) AS buyers_n,
SUM(CASE WHEN orders_n >= 2 THEN 1 ELSE 0 END) AS repeat_buyers_n,
ROUND(100.0 * SUM(CASE WHEN orders_n >= 2 THEN 1 ELSE 0 END) /
NULLIF(COUNT(*), 0), 2) AS repeat_buyers_pct
FROM delivered;
| buyers_n | repeat_buyers_n | repeat_buyers_pct |
|---|---|---|
| 3 | 1 | 33.33 |
Если делить на всех четырёх зарегистрированных клиентов, получится 25%. Это тоже математически верное число, но уже другая метрика. В отчёте всегда указывайте аудиторию и временное окно.
3. Первая и вторая доставка каждого клиента
Для анализа интервала между покупками нужен порядок событий. В оконной функции явно сортируем по дате и ID заказа на случай одинакового времени:
WITH numbered AS (
SELECT customer_id, order_id, created_at,
ROW_NUMBER() OVER (
PARTITION BY customer_id
ORDER BY created_at, order_id
) AS purchase_n
FROM orders
WHERE status = 'delivered'
)
SELECT customer_id, order_id, purchase_n
FROM numbered
ORDER BY customer_id, purchase_n;
Клиент №1 получит номера 1 и 2. Борис и Вера — только номер 1. В отличие от GROUP BY, оконная функция сохраняет отдельные строки заказов.
4. Получить даты первой и второй покупки
WITH numbered AS (
SELECT customer_id, order_id, created_at,
ROW_NUMBER() OVER (
PARTITION BY customer_id
ORDER BY created_at, order_id
) AS purchase_n
FROM orders
WHERE status = 'delivered'
),
first_second AS (
SELECT customer_id,
MIN(CASE WHEN purchase_n=1 THEN created_at END) AS first_at,
MIN(CASE WHEN purchase_n=2 THEN created_at END) AS second_at
FROM numbered
GROUP BY customer_id
)
SELECT customer_id, first_at, second_at
FROM first_second
WHERE second_at IS NOT NULL
ORDER BY customer_id;
Результат — только клиент №1 с датами 2026-09-01 11:20:00 и 2026-09-03 12:30:00. Здесь интервалы будут смысловыми только при одинаковой временной зоне и надёжных датах статусов.
Попрактикуйтесь
Сначала решите задачу на покупателей с несколькими заказами, затем проверьте собственный вариант метрики в песочнице.
Для последовательной практики на MySQL 8 — курс «SQL для аналитика» на Stepik с итоговой работой и возможностью получить сертификат.