Как посчитать MRR Churn в SQL
NULL. Какой фильтр выберет именно активные (не отменённые) заказы?Содержание:
Зачем MRR Churn
В SaaS-компании отвалилось 5% клиентов за месяц — кажется норма. Но выручка упала на 15%. Аналитик смотрит: отвалились enterprise-клиенты с высоким чеком, остались мелкие. Customer churn 5%, MRR churn 15%. Реальная проблема видна только через метрику выручки.
MRR Churn — главная метрика SaaS-бизнесов. В статье — SQL и нюансы (gross vs net, customer vs revenue).
Что такое MRR Churn
MRR Churn Rate — доля MRR, потерянная за период (отказы, downgrade).
Gross MRR Churn (%) = lost MRR / MRR at start × 100%
Net MRR Churn (%) = (lost MRR - new expansion MRR) / MRR at start × 100%Net Churn может быть отрицательным — это «negative churn», когда expansion компенсирует отток. Это золотой стандарт SaaS.
Базовый расчёт
Данные: subscriptions(user_id, mrr, started_at, ended_at, status).
WITH start_mrr AS (
SELECT SUM(mrr) AS mrr_start
FROM subscriptions
WHERE started_at <= '2026-04-01'
AND (ended_at IS NULL OR ended_at >= '2026-04-01')
),
churned_mrr AS (
SELECT SUM(mrr) AS mrr_churned
FROM subscriptions
WHERE ended_at >= '2026-04-01' AND ended_at < '2026-05-01'
)
SELECT
s.mrr_start,
c.mrr_churned,
c.mrr_churned::NUMERIC / NULLIF(s.mrr_start, 0) * 100 AS gross_mrr_churn_pct
FROM start_mrr s
CROSS JOIN churned_mrr c;Важно: churn считается на начало периода, не на конец. Иначе будете занижать.
Gross vs Net Churn
WITH start_mrr AS (
SELECT SUM(mrr) AS mrr_start
FROM subscriptions
WHERE started_at <= '2026-04-01'
AND (ended_at IS NULL OR ended_at >= '2026-04-01')
),
churned AS (
SELECT SUM(mrr) AS mrr_lost
FROM subscriptions
WHERE ended_at >= '2026-04-01' AND ended_at < '2026-05-01'
),
expansion AS (
SELECT SUM(mrr_delta) AS mrr_gained
FROM subscription_upgrades
WHERE upgrade_date >= '2026-04-01' AND upgrade_date < '2026-05-01'
AND mrr_delta > 0
),
contraction AS (
SELECT SUM(ABS(mrr_delta)) AS mrr_lost_downgrade
FROM subscription_upgrades
WHERE upgrade_date >= '2026-04-01' AND upgrade_date < '2026-05-01'
AND mrr_delta < 0
)
SELECT
s.mrr_start,
c.mrr_lost + ct.mrr_lost_downgrade AS total_lost,
e.mrr_gained,
(c.mrr_lost + ct.mrr_lost_downgrade)::NUMERIC / NULLIF(s.mrr_start, 0) * 100 AS gross_churn_pct,
(c.mrr_lost + ct.mrr_lost_downgrade - e.mrr_gained)::NUMERIC / NULLIF(s.mrr_start, 0) * 100 AS net_churn_pct
FROM start_mrr s
CROSS JOIN churned c
CROSS JOIN expansion e
CROSS JOIN contraction ct;Customer vs Revenue Churn
WITH start_state AS (
SELECT
COUNT(*) AS customers_start,
SUM(mrr) AS mrr_start
FROM subscriptions
WHERE started_at <= '2026-04-01'
AND (ended_at IS NULL OR ended_at >= '2026-04-01')
),
churned AS (
SELECT
COUNT(*) AS customers_lost,
SUM(mrr) AS mrr_lost
FROM subscriptions
WHERE ended_at >= '2026-04-01' AND ended_at < '2026-05-01'
)
SELECT
s.customers_start,
s.mrr_start,
c.customers_lost,
c.mrr_lost,
c.customers_lost::NUMERIC * 100 / NULLIF(s.customers_start, 0) AS customer_churn_pct,
c.mrr_lost::NUMERIC * 100 / NULLIF(s.mrr_start, 0) AS mrr_churn_pct
FROM start_state s
CROSS JOIN churned c;Customer churn ≠ MRR churn. Если уходят высокотарифные — MRR churn выше.
MRR Churn по сегментам
WITH start_mrr_seg AS (
SELECT plan_type, SUM(mrr) AS mrr_start
FROM subscriptions
WHERE started_at <= '2026-04-01'
AND (ended_at IS NULL OR ended_at >= '2026-04-01')
GROUP BY plan_type
),
churned_seg AS (
SELECT plan_type, SUM(mrr) AS mrr_lost
FROM subscriptions
WHERE ended_at >= '2026-04-01' AND ended_at < '2026-05-01'
GROUP BY plan_type
)
SELECT
s.plan_type,
s.mrr_start,
COALESCE(c.mrr_lost, 0) AS mrr_lost,
COALESCE(c.mrr_lost, 0)::NUMERIC * 100 / NULLIF(s.mrr_start, 0) AS churn_pct
FROM start_mrr_seg s
LEFT JOIN churned_seg c ON c.plan_type = s.plan_type
ORDER BY churn_pct DESC;Частые ошибки
Ошибка 1. Считать на начало или на конец? Только на начало периода. На конец будет иное.
Ошибка 2. Включать новых, которые в этом же месяце отвалились. Если клиент пришёл 3 апреля и ушёл 25 апреля — это churn? В стандартной логике — нет, он не был в MRR на начало. Но «micro-churn» нужно отдельно отслеживать.
Ошибка 3. Пересчёт в годовой churn.
Месячный churn 3% × 12 ≠ 36% годовых. Правильно: 1 - (1-monthly)^12 = 1 - 0,97^12 ≈ 30,6%.
Ошибка 4. Игнорировать downgrade. Клиент не ушёл, а перешёл с $100 на $30 — это $70 contraction MRR.
Ошибка 5. NULL в ended_at.
Активная подписка имеет ended_at IS NULL. Не путать с пропавшими данными.
Ошибка 6. Не различать voluntary vs involuntary churn. Voluntary — клиент сам отписался. Involuntary — карта не прошла платёж. Разные причины, разные способы решения.
Связанные темы
- Как посчитать MRR в SQL
- Как посчитать NRR в SQL
- Как посчитать churn в SQL
- Как посчитать retention в SQL
FAQ
Какой MRR Churn считается хорошим?
Ориентиры зависят от сегмента. Для B2B-SaaS 0,5–1% в месяц — отлично, 2–3% — средне, а 5% и выше — уже проблема. В B2C-SaaS планка мягче: там 3–7% в месяц считается нормой.
Net Churn может быть отрицательным?
Да, и это лучший из возможных сценариев. Отрицательный net churn (negative churn) означает, что расширение существующих клиентов — апгрейды и доплаты — приносит больше денег, чем теряется на оттоке. Проще говоря, база сама растёт по выручке, даже если кто-то уходит.
Когда смотреть на customer churn, а когда на MRR churn?
Customer churn показывает, сколько клиентов вы удержали, MRR churn — сколько денег. Если тарифы у всех примерно одинаковые, метрики почти совпадают. Но в энтерпрайзе, где чеки сильно разные, важнее MRR churn: потерять одного крупного клиента больнее, чем десяток мелких.
Чем voluntary churn отличается от involuntary?
Voluntary churn — когда клиент уходит сам: продукт не подошёл или он ушёл к конкуренту. Involuntary churn — техническая причина: не прошла оплата, банк заблокировал карту. Это разные проблемы: с добровольным оттоком работает продукт, с техническим — биллинг и дуннинг-цепочки.
Annual MRR churn = monthly × 12?
Нет, умножать на 12 нельзя — отток накапливается мультипликативно. Правильная формула: Annual = 1 - (1-monthly)^12. Например, при 3% в месяц годовой churn выходит 30,6%, а не 36%.