Условие
Таблица sf_events(account_id, user_id, record_date) — лог активности. Посчитайте retention rate для каждого account_id за декабрь 2020 → январь 2021: какая доля декабрьских юзеров активна и в январе.
Выход: account_id, retention_rate.
Формула: unique_users_in_both / unique_users_in_dec.
Решение
WITH dec_users AS (
SELECT DISTINCT account_id, user_id
FROM sf_events
WHERE record_date BETWEEN '2020-12-01' AND '2020-12-31'
),
jan_users AS (
SELECT DISTINCT account_id, user_id
FROM sf_events
WHERE record_date BETWEEN '2021-01-01' AND '2021-01-31'
)
SELECT
d.account_id,
ROUND(
COUNT(DISTINCT j.user_id)::DECIMAL / COUNT(DISTINCT d.user_id),
2
) AS retention_rate
FROM dec_users d
LEFT JOIN jan_users j
ON j.account_id = d.account_id
AND j.user_id = d.user_id
GROUP BY d.account_id;Идея
LEFT JOIN декабрьских с январскими. COUNT(DISTINCT j.user_id) посчитает только тех, кто нашёлся в обоих периодах (NULL не считается).
Альтернатива через CASE
SELECT
account_id,
ROUND(
SUM(CASE WHEN active_dec AND active_jan THEN 1 ELSE 0 END)::DECIMAL
/ NULLIF(SUM(CASE WHEN active_dec THEN 1 ELSE 0 END), 0),
2
) AS retention_rate
FROM (
SELECT
account_id, user_id,
MAX(CASE WHEN record_date BETWEEN '2020-12-01' AND '2020-12-31' THEN 1 END) AS active_dec,
MAX(CASE WHEN record_date BETWEEN '2021-01-01' AND '2021-01-31' THEN 1 END) AS active_jan
FROM sf_events
GROUP BY account_id, user_id
) t
GROUP BY account_id;Подводные камни
account_idс 0 декабрьскими пользователями. Знаменатель = 0 → деление на NULL. ЧерезNULLIFспасаем.- Один user активен в обе декабрь и в феврале (но не в январе). Не должен попадать в числитель — фильтр строго по январю.
- Часовой пояс. Если
record_date TIMESTAMP UTC, а аналитик считает по локали — границы декабрь/январь могут «съехать». - DISTINCT vs не. Если в декабре юзер активен 30 раз — это всё ещё один активный.
DISTINCT user_id.
Эталонный ответ
LEFT JOIN дек/янв по (account_id, user_id) → COUNT(DISTINCT) численитель/знаменатель, NULLIF против деления на 0. Округление до 2.