Как посчитать MRR в SQL
Карьерник — квиз-тренажёр в Telegram с 1500+ вопросами для собесов аналитика. SQL, Python, A/B, метрики. Бесплатно.
Зачем аналитику считать MRR в SQL
MRR (Monthly Recurring Revenue) — главная метрика любого SaaS-бизнеса. По ней меряется здоровье продукта, её смотрят инвесторы, по ней распределяют бюджеты. Growth rate = (MRR_new − MRR_old) / MRR_old — именно это число стоит в питчдеках.
Посчитать MRR кажется простым: «сумма активных подписок за месяц». На практике всплывают десятки нюансов. Годовые подписки нужно нормализовать в месячный эквивалент (или MRR будет скакать в месяц оплаты). Prorated подписки (неполный месяц) считаются пропорционально. Trial не идёт в MRR до конвертации. Discounts уменьшают MRR, но только в период действия.
А ещё MRR movements: new, expansion, contraction, churn. Без этой декомпозиции вы не знаете, растёт ли продукт или просто набирает новых клиентов через маркетинг. На собеседовании для SaaS-компаний это одна из первых задач.
В статье — готовые SQL-запросы:
- Базовый MRR на конкретную дату
- MRR по тарифам и сегментам
- Normalized MRR из годовых подписок
- MRR movements (New / Expansion / Contraction / Churn)
- Net Revenue Retention (NRR) — ключевая SaaS-метрика
- Cohort MRR retention
Схема: subscriptions(user_id, plan, mrr, status, started_at, ended_at).
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. Normalized MRR из годовых подписок
Если плата раз в год — нельзя её целиком отнести на один месяц. Нормализуем:
SELECT
user_id,
CASE
WHEN billing_period = 'monthly' THEN amount
WHEN billing_period = 'annual' THEN amount / 12.0
WHEN billing_period = 'quarterly' THEN amount / 3.0
END AS mrr_normalized
FROM subscriptions
WHERE status = 'active';4. MRR по месяцам (time series)
WITH months AS (
SELECT generate_series('2025-01-01'::DATE, '2026-04-01'::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,
(mrr - LAG(mrr) OVER (ORDER BY month)) /
NULLIF(LAG(mrr) OVER (ORDER BY month), 0) * 100 AS mom_growth_pct
FROM mrr_snapshots
ORDER BY month;5. MRR movements
Разложение изменения MRR на компоненты — must-have для SaaS-отчёта.
WITH events AS (
SELECT
DATE_TRUNC('month', event_at) AS month,
CASE
WHEN event_type = 'start' THEN 'new'
WHEN event_type = 'upgrade' THEN 'expansion'
WHEN event_type = 'downgrade' THEN 'contraction'
WHEN event_type = 'cancel' THEN 'churn'
END AS movement_type,
mrr_delta
FROM mrr_events
WHERE DATE_TRUNC('month', event_at) = '2026-04-01'
)
SELECT
movement_type,
SUM(mrr_delta) AS mrr_change,
COUNT(*) AS movements_count
FROM events
GROUP BY movement_type;6. Net Revenue Retention (NRR)
WITH start_mrr AS (
SELECT user_id, mrr
FROM subscriptions_snapshot
WHERE snapshot_date = '2026-01-01'
),
end_mrr AS (
SELECT user_id, mrr
FROM subscriptions_snapshot
WHERE snapshot_date = '2026-04-01'
)
SELECT
SUM(COALESCE(e.mrr, 0)) / SUM(s.mrr) AS nrr
FROM start_mrr s
LEFT JOIN end_mrr e USING (user_id);NRR > 100% — expansion перекрывает churn (идеал). NRR 95-100% — норма для B2B SaaS. NRR < 90% — тревожно.
7. Cohort MRR retention
WITH cohorts AS (
SELECT user_id, DATE_TRUNC('month', first_payment_at) AS cohort_month
FROM subscriptions
),
snapshots AS (
SELECT
c.cohort_month,
DATE_TRUNC('month', s.snapshot_date) AS snapshot_month,
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 * FROM snapshots
ORDER BY cohort_month, months_since_start;Частые ошибки
Путать MRR и cash
MRR — recurring. Единоразовые платежи (setup fee) в MRR не идут.
Не нормализовать annual
Годовая подписка 12 000 ₽ → MRR 1 000 ₽/мес, не 12 000 в апреле и 0 в остальные.
Считать MRR для trial
Trial в MRR не входит. Когда конвертируется в paid — тогда.
Discounts на весь период
Discount 50% на 3 месяца снижает MRR только на эти 3 месяца, потом возвращается.
Связанные темы
FAQ
MRR или ARR?
MRR для operational. ARR = MRR × 12 для отчётов инвесторам.
Как учесть refunds?
Вычитать из MRR месяца, когда случился refund.
Trial + discounts — как?
Trial не в MRR. Discount — фактическая MRR после скидки.
MRR для usage-based pricing?
Сложно — доход нестабильный. Используют «committed MRR» (минимальный) + «expansion MRR» (overage).
Тренируйте SQL — откройте тренажёр с 1500+ вопросами для собесов.