SQL Gym Pro #1 - Разбор 💪 Ну что, разбираем решение!

💎****Основное решение Логика простая:

Находим дату первой активности = регистрация Считаем разницу в днях между активностями и регистрацией Проверяем, попадает ли в нужные окна (6-8 дней, 29-31 день)

WITH first_activity AS ( SELECT user_id, MIN(activity_date) as reg_date FROM user_activity WHERE activity_date BETWEEN '2024-01-01' AND '2024-01-31' GROUP BY user_id )

SELECT COUNT(DISTINCT fa.user_id) as total_users,

COUNT(DISTINCT CASE WHEN DATEDIFF(ua.activity_date, fa.reg_date) BETWEEN 6 AND 8 THEN ua.user_id END) as day7_retained,

COUNT(DISTINCT CASE WHEN DATEDIFF(ua.activity_date, fa.reg_date) BETWEEN 29 AND 31 THEN ua.user_id END) as day30_retained,

ROUND(100.0 * COUNT(DISTINCT CASE WHEN DATEDIFF(ua.activity_date, fa.reg_date) BETWEEN 6 AND 8 THEN ua.user_id END) / COUNT(DISTINCT fa.user_id), 2) as ret_7d_pct,

ROUND(100.0 * COUNT(DISTINCT CASE WHEN DATEDIFF(ua.activity_date, fa.reg_date) BETWEEN 29 AND 31 THEN ua.user_id END) / COUNT(DISTINCT fa.user_id), 2) as ret_30d_pct

FROM first_activity fa LEFT JOIN user_activity ua ON fa.user_id = ua.user_id AND ua.activity_date > fa.reg_date

😎 Ключевые моменты:

  • DISTINCT чтобы юзер не считался дважды

  • 100.0 * чтобы не потерять проценты

  • LEFT JOIN чтобы учесть всех, даже кто не вернулся

🔥****Усложнение: минимум 2 дня активности WITH first_activity AS ( SELECT user_id, MIN(activity_date) as reg_date FROM user_activity WHERE activity_date BETWEEN '2024-01-01' AND '2024-01-31' GROUP BY user_id ), activity_count AS ( SELECT fa.user_id, COUNT(DISTINCT CASE WHEN DATEDIFF(ua.activity_date, fa.reg_date) BETWEEN 6 AND 8 THEN ua.activity_date END) as days_7, COUNT(DISTINCT CASE WHEN DATEDIFF(ua.activity_date, fa.reg_date) BETWEEN 29 AND 31 THEN ua.activity_date END) as days_30 FROM first_activity fa LEFT JOIN user_activity ua ON fa.user_id = ua.user_id GROUP BY fa.user_id )

SELECT COUNT() as total, SUM(CASE WHEN days_7 >= 2 THEN 1 ELSE 0 END) as ret_7d, SUM(CASE WHEN days_30 >= 2 THEN 1 ELSE 0 END) as ret_30d, ROUND(100.0 * SUM(CASE WHEN days_7 >= 2 THEN 1 ELSE 0 END) / COUNT(), 2) as ret_7d_pct FROM activity_count Тут считаем уникальные дни активности, потом фильтруем тех, у кого >= 2 дней.

Запомнить 📝

  • Retention = вернулся ли юзер после N дней

  • CASE WHEN внутри COUNT - очень удобно

  • Всегда думайте про edge cases

Следующая задачка - в пятницу! 💪