Как посчитать повторные покупки клиентов через SQL

У покупателя два заказа. Это уже повторная покупка — или одна отмена и одна доставка? Перед SQL важно договориться о статусах, аудитории и периоде.

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_nrepeat_buyers_nrepeat_buyers_pct
3133.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 с итоговой работой и возможностью получить сертификат.