Как посчитать D1, D7, D30 retention в SQL
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;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_rollingRolling 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 показывает реальную ценность продукта.
Это официальная информация?
Нет, это не официальная информация. Статья опирается на общепринятые индустриальные практики, а не на документацию конкретного продукта.