Как посчитать MRR в SQL
new_users_ru(user_id) и new_users_kz(user_id). Нужно получить общий список пользователей для дальнейшего соединения с orders. Что сделать на первом шаге?Содержание:
Зачем аналитику считать MRR в SQL
MRR (Monthly Recurring Revenue) — ключевая метрика любого SaaS-продукта. По ней меряется здоровье бизнеса, она идёт в инвестиционные отчёты и в обсуждения с бордом. Ошибки в расчёте MRR почти всегда приводят к неверным выводам о росте.
Посчитать MRR кажется простым: «сумма активных подписок на дату». На практике возникают ровно те нюансы, на которых и спотыкаются:
- годовые подписки нужно нормализовать в месячный эквивалент, иначе MRR скачет в месяц оплаты;
- prorated-подписки (неполный месяц) считаются пропорционально количеству оплаченных дней;
- триалы в MRR не идут до конвертации;
- скидки учитываются только в период их действия.
А ещё стандартная декомпозиция — MRR movements: new / expansion / contraction / churn. Без неё непонятно, растёт ли продукт за счёт реальной стоимости или только за счёт верхней воронки.
В статье — готовые SQL-запросы под частые задачи.
Схема данных
subscriptions (user_id, plan, mrr, billing_period, status, started_at, ended_at)
mrr_events (user_id, event_type, event_at, mrr_delta)
mrr_snapshots (user_id, snapshot_date, mrr)1. MRR на конкретную дату
SELECT
DATE '2026-04-01' AS snapshot_date,
SUM(mrr) AS total_mrr
FROM subscriptions
WHERE status = 'active'
AND started_at <= '2026-04-01'
AND (ended_at IS NULL OR ended_at > '2026-04-01');Подписка попадает в MRR, если активна на snapshot-дату.
2. MRR по тарифам
SELECT
plan,
COUNT(*) AS active_subs,
SUM(mrr) AS total_mrr,
AVG(mrr) AS avg_mrr_per_sub
FROM subscriptions
WHERE status = 'active'
GROUP BY plan
ORDER BY total_mrr DESC;3. Нормализованный MRR для разных тарифных периодов
Если у вас перемешаны monthly / quarterly / annual — нормализуем:
SELECT
user_id,
plan,
CASE
WHEN billing_period = 'monthly' THEN amount
WHEN billing_period = 'quarterly' THEN amount / 3.0
WHEN billing_period = 'annual' THEN amount / 12.0
END AS mrr_normalized
FROM subscriptions
WHERE status = 'active';Годовая подписка на 12 000 ₽ превращается в MRR 1 000 ₽ — это единственный честный способ показывать динамику MRR, если у части клиентов разные периоды оплаты.
4. MRR по месяцам (time series)
WITH months AS (
SELECT generate_series(
'2025-01-01'::DATE,
DATE_TRUNC('month', CURRENT_DATE)::DATE,
'1 month'
)::DATE AS month
),
mrr_snapshots AS (
SELECT
m.month,
SUM(s.mrr) AS mrr
FROM months m
LEFT JOIN subscriptions s
ON s.started_at <= m.month
AND (s.ended_at IS NULL OR s.ended_at > m.month)
AND s.status != 'cancelled'
GROUP BY m.month
)
SELECT
month,
mrr,
LAG(mrr) OVER (ORDER BY month) AS prev_mrr,
100.0 * (mrr - LAG(mrr) OVER (ORDER BY month))
/ NULLIF(LAG(mrr) OVER (ORDER BY month), 0) AS mom_growth_pct
FROM mrr_snapshots
ORDER BY month;С generate_series пустые месяцы не «пропадают» из отчёта — это важно для честного графика.
5. MRR movements
Классическая SaaS-разбивка изменения MRR:
SELECT
DATE_TRUNC('month', event_at)::DATE AS month,
SUM(CASE WHEN event_type = 'start' THEN mrr_delta ELSE 0 END) AS new_mrr,
SUM(CASE WHEN event_type = 'upgrade' THEN mrr_delta ELSE 0 END) AS expansion,
SUM(CASE WHEN event_type = 'downgrade' THEN -mrr_delta ELSE 0 END) AS contraction,
SUM(CASE WHEN event_type = 'cancel' THEN -mrr_delta ELSE 0 END) AS churn
FROM mrr_events
GROUP BY 1
ORDER BY 1;Это и есть ответ на вопрос «за счёт чего вырос MRR»: новые клиенты, расширение существующих, а что потеряли — в downgrade и churn.
6. Net Revenue Retention (NRR)
NRR измеряется на существующей когорте клиентов и показывает, как изменился их MRR за период:
WITH start_mrr AS (
SELECT user_id, mrr
FROM mrr_snapshots
WHERE snapshot_date = '2026-01-01'
),
end_mrr AS (
SELECT user_id, mrr
FROM mrr_snapshots
WHERE snapshot_date = '2026-04-01'
)
SELECT
SUM(COALESCE(e.mrr, 0)) / NULLIF(SUM(s.mrr), 0) AS nrr
FROM start_mrr s
LEFT JOIN end_mrr e USING (user_id);Ориентиры:
- > 100% — расширение существующих клиентов перекрывает отток (редкая здоровая ситуация);
- 95–100% — типовой диапазон для зрелых B2B SaaS;
- < 90% — повод разобраться, почему уходят и не расширяются.
7. Когортный MRR retention
WITH cohorts AS (
SELECT
user_id,
DATE_TRUNC('month', first_payment_at)::DATE AS cohort_month
FROM subscriptions
),
snapshots AS (
SELECT
c.cohort_month,
DATE_TRUNC('month', s.snapshot_date)::DATE AS snapshot_month,
(EXTRACT(YEAR FROM AGE(s.snapshot_date, c.cohort_month)) * 12
+ EXTRACT(MONTH FROM AGE(s.snapshot_date, c.cohort_month)))::INT AS months_since_start,
SUM(s.mrr) AS mrr_in_cohort
FROM cohorts c
JOIN mrr_snapshots s USING (user_id)
GROUP BY 1, 2, 3
)
SELECT
cohort_month,
months_since_start,
mrr_in_cohort
FROM snapshots
ORDER BY cohort_month, months_since_start;EXTRACT(MONTH FROM AGE(...)) в Postgres возвращает только компонент месяцев (0–11), поэтому собираем целое число через годы × 12 + месяцы — иначе данные за полтора года превратятся в «6 месяцев».
Частые ошибки
1. Путать MRR и cash
MRR измеряет recurring-составляющую дохода. Единоразовые платежи (setup fee, onboarding fee) в MRR не попадают, иначе картина роста будет рваной.
2. Не нормализовать годовые подписки
Годовая подписка в 12 000 ₽ — это MRR 1 000 ₽, а не 12 000 ₽ в месяц оплаты и 0 ₽ в остальные.
3. Триал в MRR
Триал до конверсии в платный план не считается. Иначе MRR будет раздуваться там, где никто ещё не платит.
4. Discounts
Скидка 50% на три месяца уменьшает MRR только на эти три месяца. Потом MRR возвращается к базовой стоимости (если не уехал в churn).
5. Путать NRR и Gross Revenue Retention
NRR учитывает expansion, Gross Revenue Retention — только потери (contraction + churn). Показывайте в отчёте обе метрики, они отвечают на разные вопросы.
Связанные темы
- MRR и ARR простыми словами
- NRR простыми словами
- Как посчитать churn в SQL
- SQL для subscription-бизнеса
FAQ
MRR или ARR?
MRR удобнее для оперативного мониторинга, ARR (MRR × 12) — для board-репортов и годовых презентаций.
Как учитывать возвраты?
Refund вычитается из MRR того месяца, в котором он случился. Если refund на весь период — в месяце возврата подписка должна считаться закрытой.
Usage-based pricing — как считать MRR?
Для тарифов с плавающим потреблением выделяют committed MRR (минимальный гарантированный платёж) и expansion MRR (overage за фактическое потребление).
Как сверить MRR с бухгалтерией?
Удобно держать отчёт, в котором в одном разрезе показаны MRR, billed amount и collected cash — расхождения сразу бросаются в глаза.