Собесов

Zachary Thomas: month-over-month MAU

SQLОконные функцииСредняяMiddle

Условие

Дана таблица 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;

Подводные камни

  1. Пропущенные месяцы в логинах ломают расчёт: LAG возьмёт месяц через два, а не «предыдущий календарный». Если важно — генерируйте серию месяцев через generate_series и LEFT JOIN на агрегацию.
  2. NULLIF(prev, 0) обязателен — иначе деление на ноль в первый месяц.
  3. COUNT(DISTINCT user_id) дороже, чем COUNT(*) — на больших таблицах считайте через предварительную дедупликацию.
  4. Часовой пояс: DATE_TRUNC зависит от типа колонки и tz сессии. Согласуйте с бизнесом, что такое «месяц».

Эталонный ответ

Сначала агрегируйте MAU по месяцам, потом LAG() по упорядоченным месяцам и считайте (curr - prev) / prev * 100 с защитой от деления на ноль через NULLIF.

Хочешь увидеть разбор?

Зарегистрируйся бесплатно — откроется развёрнутое решение этой задачи и ещё 4 на выбор.

Зарегистрироваться и увидеть разбор
Уже есть аккаунт? Войти