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

Закрепи формулу d1 d7 d30 retention в Карьернике
Запомнить надолго — 5 коротких сессий с задачами на эту тему. Бесплатно
Тренировать d1 d7 d30 retention в Telegram

Что такое 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 — потому что включает больший интервал.

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

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

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

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

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

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

FAQ

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

Зависит от бенчмарка категории. Для соцсетей плохо (норма 50%+). Для b2b SaaS — нормально. Для приложения раз в месяц — отлично.

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

D1 показывает onboarding. D7 показывает habit formation. Оба важны, но если выбирать один — D7, потому что D1 можно «починить» лёгкими twiks, а D7 показывает реальную ценность.

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

Нет. Статья основана на индустриальных практиках.