Как посчитать MoM в SQL

Закрепи формулу mom в Карьернике
Запомнить надолго — 5 коротких сессий с задачами на эту тему. Бесплатно
Тренировать mom в Telegram

Зачем MoM

Менеджер: «Выручка в апреле упала на 5% — что делать?». Аналитик смотрит MoM по году: каждый апрель -3-7% относительно марта — это сезонная нормальность (праздники, корпоративные траты завершаются). Без MoM-контекста реакция была бы паникой.

MoM — рабочая метрика для месячных циклов: SaaS-подписки, биллинг, маркетинговые кампании. В статье — SQL через LAG и нюансы.

Формула MoM

MoM growth (%) = (current_month / previous_month - 1) × 100%
MoM abs       = current_month - previous_month

В отличие от WoW (понедельная), MoM сглаживает дневной шум, но не учитывает сезонность месяца.

Базовый расчёт

Данные: daily_revenue(day, revenue).

WITH monthly AS (
    SELECT
        DATE_TRUNC('month', day) AS month,
        SUM(revenue) AS revenue
    FROM daily_revenue
    WHERE day >= CURRENT_DATE - INTERVAL '24 months'
    GROUP BY 1
)
SELECT
    month,
    revenue,
    LAG(revenue) OVER (ORDER BY month) AS prev_month,
    revenue - LAG(revenue) OVER (ORDER BY month) AS mom_abs,
    (revenue::NUMERIC / NULLIF(LAG(revenue) OVER (ORDER BY month), 0) - 1) * 100 AS mom_pct
FROM monthly
ORDER BY month;

Важно: DATE_TRUNC('month', ...) группирует по календарному месяцу. Если месяц неполный — MoM может быть искажён.

MoM по сегментам

WITH monthly_cat AS (
    SELECT
        DATE_TRUNC('month', created_at) AS month,
        category,
        SUM(total) AS revenue
    FROM orders
    WHERE status = 'paid'
    GROUP BY 1, 2
)
SELECT
    month,
    category,
    revenue,
    (revenue::NUMERIC / NULLIF(
        LAG(revenue) OVER (PARTITION BY category ORDER BY month), 0
    ) - 1) * 100 AS mom_pct
FROM monthly_cat
ORDER BY category, month;
Закрепи формулу mom в Карьернике
Запомнить надолго — 5 коротких сессий с задачами на эту тему. Бесплатно
Тренировать mom в Telegram

MoM vs WoW vs YoY

Метрика Сравнение Когда
WoW неделя vs предыдущая Высокочастотные, weekly tracking
MoM месяц vs предыдущий SaaS, биллинг, подписки
YoY период vs тот же год назад Сезонные бизнесы

В сезонных категориях MoM может вводить в заблуждение — Q4 vs Q3 всегда «растёт» в ритейле, но это не успех команды.

Сглаживание MoM

3-month rolling avg смягчает разовые провалы:

WITH monthly AS (
    SELECT
        DATE_TRUNC('month', created_at) AS month,
        SUM(total) AS revenue
    FROM orders
    WHERE status = 'paid'
    GROUP BY 1
)
SELECT
    month,
    revenue,
    AVG(revenue) OVER (
        ORDER BY month
        ROWS BETWEEN 2 PRECEDING AND CURRENT ROW
    ) AS revenue_3m_avg
FROM monthly
ORDER BY month;

Частые ошибки

Ошибка 1. Сравнивать неполный месяц. Сегодня 13 мая. «MoM = май vs апрель» — но май ещё не закончился. Лучше month-to-date vs same period prior month.

Ошибка 2. Integer division. В Postgres revenue / prev_revenue для INTEGER даёт целое. Используйте ::NUMERIC или 1.0 *.

Ошибка 3. Сезонность. В ритейле декабрь всегда +30% к ноябрю. MoM растёт, но это сезон. YoY лучше для сезонных.

Ошибка 4. NULL при первом месяце. LAG возвращает NULL для первой строки. Это корректно — не заполняйте нулями.

Ошибка 5. Timezone. DATE_TRUNC в UTC может разбить месяц иначе, чем в МСК. Приводите к нужной timezone.

Связанные темы

FAQ

MoM или WoW?

В SaaS / подписке — MoM. В высокочастотном продукте (e-com, mobility) — WoW. В сезонных — YoY.

Что делать с неполным месяцем?

Month-to-date vs same period prior month. Или waiting until полный.

MoM может быть отрицательным?

Да. -5% — норма для сезонного спада, -20% — серьёзный сигнал.

Какие изменения большие?

В growth-стартапе +5-15% MoM — норма. В зрелом бизнесе ±2-3% — обычные колебания.

MoM на пользовательских метриках?

DAU не имеет смысла мерить MoM — это daily. MAU — да, MoM правильно.