Условие
Дана таблица logins(user_id, login_date). Напишите запрос, который для каждого месяца возвращает MAU (число уникальных пользователей за месяц) и месяц-к-месяцу изменение MAU в процентах.
Решение
Подход
Шаг 1: агрегировать число уникальных пользователей по месяцу. Шаг 2: на агрегированной выборке использовать LAG(), чтобы вытащить MAU предыдущего месяца. Шаг 3: посчитать процент изменения. Так делать корректнее, чем LAG() поверх сырых логинов: пропуски месяцев в данных не сломают окно по дате-месяцу.
Реализация
WITH monthly AS (
SELECT
DATE_TRUNC('month', login_date)::date AS month,
COUNT(DISTINCT user_id) AS mau
FROM logins
GROUP BY 1
)
SELECT
month,
mau,
LAG(mau) OVER (ORDER BY month) AS prev_mau,
ROUND(
100.0 * (mau - LAG(mau) OVER (ORDER BY month))
/ NULLIF(LAG(mau) OVER (ORDER BY month), 0),
2
) AS mom_pct
FROM monthly
ORDER BY month;Подводные камни
- Пропущенные месяцы в логинах ломают расчёт:
LAGвозьмёт месяц через два, а не «предыдущий календарный». Если важно — генерируйте серию месяцев черезgenerate_seriesиLEFT JOINна агрегацию. NULLIF(prev, 0)обязателен — иначе деление на ноль в первый месяц.COUNT(DISTINCT user_id)дороже, чемCOUNT(*)— на больших таблицах считайте через предварительную дедупликацию.- Часовой пояс:
DATE_TRUNCзависит от типа колонки и tz сессии. Согласуйте с бизнесом, что такое «месяц».
Эталонный ответ
Сначала агрегируйте MAU по месяцам, потом LAG() по упорядоченным месяцам и считайте (curr - prev) / prev * 100 с защитой от деления на ноль через NULLIF.