SQLАналитические паттерныHardБесплатно

Когортный retention: Day 7 и Day 30

// Условие

Для каждой когорты (месяц регистрации, формат YYYY-MM) посчитайте:
— количество пользователей в когорте (cohort_size)
— долю тех, кто совершил хотя бы одну завершённую покупку через 7 и более дней после регистрации (retention_d7)
— долю тех, кто совершил завершённую покупку через 30 и более дней (retention_d30)
Округлите доли до 2 знаков (от 0.00 до 1.00). Выведите cohort_month, cohort_size, retention_d7, retention_d30. Отсортируйте по cohort_month.

// Таблицы

sandbox.users
user_idINT
registration_dateDATE
acquisition_channelVARCHAR(20)
platformVARCHAR(10)
cityVARCHAR(50)
is_premiumBOOLEAN
sandbox.orders
order_idINT
user_idINT
order_dateTIMESTAMP
amountNUMERIC(10,2)
statusVARCHAR(20)
delivery_daysINT
promo_codeVARCHAR(20)

// Что тренирует

КогортыRetentionCOUNT DISTINCT CASEDATE_TRUNC
▶ Решить в SQL-песочнице

// Решение

Подсказка

LEFT JOIN users → orders. Когорта = TO_CHAR(DATE_TRUNC('month', registration_date), 'YYYY-MM'). Retention = COUNT(DISTINCT CASE WHEN order_date >= registration_date + INTERVAL '7 days' THEN user_id END) / COUNT(DISTINCT user_id).

Сначала попробуй сам → Показать решение
SELECT
  TO_CHAR(DATE_TRUNC('month', u.registration_date), 'YYYY-MM') AS cohort_month,
  COUNT(DISTINCT u.user_id) AS cohort_size,
  ROUND(
    COUNT(DISTINCT CASE
      WHEN o.order_date >= u.registration_date + INTERVAL '7 days'
       AND o.status = 'completed'
      THEN u.user_id
    END)::numeric / COUNT(DISTINCT u.user_id),
    2
  ) AS retention_d7,
  ROUND(
    COUNT(DISTINCT CASE
      WHEN o.order_date >= u.registration_date + INTERVAL '30 days'
       AND o.status = 'completed'
      THEN u.user_id
    END)::numeric / COUNT(DISTINCT u.user_id),
    2
  ) AS retention_d30
FROM sandbox.users u
LEFT JOIN sandbox.orders o ON u.user_id = o.user_id
GROUP BY 1
ORDER BY 1;

Задача уровня финального собеседования. Ключевые моменты:
(1) LEFT JOIN — чтобы не потерять пользователей без заказов (они входят в знаменатель).
(2) COUNT(DISTINCT CASE WHEN ... THEN user_id END) — условная агрегация. Считаем уникальных пользователей, у которых есть заказ через 7+ дней.
(3) ::numeric перед делением — без этого PostgreSQL сделает целочисленное деление и вернёт 0.
(4) Последние когорты будут иметь retention_d30 ≈ 0, потому что не прошло 30 дней — это нормально.
Связь со статьёй «Retention: как считать» на сайте.

// Похожие задачи

// Разобраться в теме

→ SQL-задачи с собеседований аналитика