Как посчитать D1, D7, D30 retention в SQL

Проверь себя · 1/3разбор после ответа
В таблице products категория хранится как Books, books, BOOKS. Нужно одним условием в WHERE отобрать все варианты категории «книги». Какой фильтр наиболее надёжен?

Что такое D1, D7, D30

D1 retention — доля пользователей, вернувшихся на 1-й день после регистрации. D7 — на 7-й, D30 — на 30-й. Базовая метрика любого продукта — без неё не понятно, нравится ли продукт пользователям.

Что считать «днём 0»: дата регистрации. Что считать «днём N»: ровно через N дней (classic) или в окне до N (rolling).

Бенчмарки для b2c-приложений (грубо):

  • D1 — 30-50%
  • D7 — 15-30%
  • D30 — 8-15%

Игры обычно выше, b2b-приложения ниже.

Classic vs rolling retention

Classic retention (день в день): пользователь должен вернуться именно на N-й день.

Rolling retention (диапазон): пользователь должен вернуться в любой день от N до N+window.

                День 0    День 1    День 7    День 30
Юзер A          signup    open      open      —          → classic D1, D7; не D30
Юзер B          signup    —         open      open       → classic D7, D30; не D1
Юзер C          signup    —         —         —          → не активен

Какой вариант использовать:

  • Classic — для строгих метрик, для сравнения с бенчмарками
  • Rolling — для b2b и редко используемых продуктов (пользователь мог пропустить именно D7, но активен)

SQL: classic D1 retention

Допустим, есть таблица events:

WITH cohort AS (
  SELECT DISTINCT user_id, signup_date::DATE AS day_0
  FROM users
  WHERE signup_date BETWEEN '2026-04-01' AND '2026-04-30'
),
returns AS (
  SELECT
    c.user_id,
    EXISTS (
      SELECT 1 FROM events e
      WHERE e.user_id = c.user_id
        AND e.created_at::DATE = c.day_0 + 1
    ) AS d1_retained
  FROM cohort c
)
SELECT
  COUNT(*) AS cohort_size,
  SUM(CASE WHEN d1_retained THEN 1 ELSE 0 END) AS retained_d1,
  ROUND(SUM(CASE WHEN d1_retained THEN 1.0 ELSE 0 END) / NULLIF(COUNT(*), 0), 3) AS d1_retention
FROM returns;
Закрепи формулу d1 d7 d30 retention в Карьернике
Запомнить надолго — 5 коротких сессий с задачами на эту тему. Бесплатно
Тренировать d1 d7 d30 retention в Telegram

SQL: D7 и D30

Универсальная функция через INTERVAL:

WITH cohort AS (
  SELECT DISTINCT user_id, signup_date::DATE AS day_0
  FROM users
  WHERE signup_date BETWEEN '2026-04-01' AND '2026-04-30'
),
retention AS (
  SELECT
    c.user_id,
    EXISTS (SELECT 1 FROM events e WHERE e.user_id = c.user_id
            AND e.created_at::DATE = c.day_0 + 1) AS d1,
    EXISTS (SELECT 1 FROM events e WHERE e.user_id = c.user_id
            AND e.created_at::DATE = c.day_0 + 7) AS d7,
    EXISTS (SELECT 1 FROM events e WHERE e.user_id = c.user_id
            AND e.created_at::DATE = c.day_0 + 30) AS d30
  FROM cohort c
)
SELECT
  COUNT(*) AS cohort_size,
  ROUND(SUM(CASE WHEN d1 THEN 1.0 ELSE 0 END) / NULLIF(COUNT(*), 0), 3) AS d1,
  ROUND(SUM(CASE WHEN d7 THEN 1.0 ELSE 0 END) / NULLIF(COUNT(*), 0), 3) AS d7,
  ROUND(SUM(CASE WHEN d30 THEN 1.0 ELSE 0 END) / NULLIF(COUNT(*), 0), 3) AS d30
FROM retention;

SQL: rolling retention

Тот же запрос с диапазоном вместо точной даты:

-- Rolling D7 — вернулся в окне D7-D13
EXISTS (
  SELECT 1 FROM events e
  WHERE e.user_id = c.user_id
    AND e.created_at::DATE BETWEEN c.day_0 + 7 AND c.day_0 + 13
) AS d7_rolling

Rolling D7 всегда выше classic D7 — потому что включает больший интервал.

Частые ошибки

Не учитывать часовой пояс. Пользователь регистрируется в 23:30 UTC, открывает приложение в 00:30 UTC следующего дня — это уже разные дни. Проблема: 30-минутный промежуток засчитывается как D1. Решение: использовать локальное время пользователя или достаточно широкую границу суток.

Считать D7 retention сегодняшних пользователей. Пользователь, зарегистрированный 3 дня назад, не имеет шанса вернуться на D7 — у него окно ещё не закрылось. Включайте только когорты, где наблюдательное окно полностью прошло.

Использовать DAU вместо события возврата. «DAU = вернулся» — если ваш DAU включает открытия из пуша без активного действия, retention завышен. Используйте конкретное событие (session_start, content_view, action).

Путать classic и rolling. На собесе уточните, какой формат от вас ждут. Бенчмарки обычно по classic.

Считать retention на смешанных когортах. Пользователи, которые регистрируются и сразу совершают целевое действие, — это органически активная когорта. Если вы запускали рекламу → пришла уже другая когорта. Раскладывайте retention по каналам привлечения.

FAQ

D7 = 30% — это хорошо или плохо?

Зависит от бенчмарка вашей категории — универсального «хорошо» здесь нет. Для соцсетей 30% — это слабо, там норма выше 50%. Для b2b SaaS такой уровень вполне нормальный, а для приложения, которым пользуются раз в месяц, это отличный результат.

Что важнее — D1 или D7?

D1 отражает качество онбординга, а D7 — формирование привычки. Оба важны, но если выбирать один, берите D7: D1 нередко удаётся вытянуть косметическими доработками, а D7 показывает реальную ценность продукта.

Это официальная информация?

Нет, это не официальная информация. Статья опирается на общепринятые индустриальные практики, а не на документацию конкретного продукта.