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

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

Зачем GRR

GRR (Gross Revenue Retention) = MRR retained, без expansion. Показывает «pure» retention — сколько денег осталось от existing base после churn + contraction. GRR < NRR всегда. GRR > 90% — healthy SaaS.

Формула

GRR = (starting_MRR - churn_MRR - contraction_MRR) / starting_MRR × 100%

Никогда > 100% (в отличие от NRR).

GRR vs NRR

Метрика Includes Expansion Может быть > 100%
GRR No No (max 100%)
NRR Yes Yes (best > 120%)

GRR clean signal от churn / downgrade quality. NRR overall health (с upsell).

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

WITH start AS (
    SELECT SUM(monthly_mrr) AS start_mrr
    FROM customers
    WHERE active_on = '2026-04-01'
),
churn_mrr AS (
    SELECT SUM(monthly_mrr) AS churned
    FROM customers
    WHERE active_on = '2026-04-01'
      AND churned_at BETWEEN '2026-04-01' AND '2026-05-01'
),
contraction AS (
    SELECT SUM(old_mrr - new_mrr) AS contracted_mrr
    FROM plan_changes
    WHERE change_at BETWEEN '2026-04-01' AND '2026-05-01'
      AND new_mrr < old_mrr
)
SELECT
    s.start_mrr,
    c.churned,
    co.contracted_mrr,
    (s.start_mrr - c.churned - co.contracted_mrr)::NUMERIC * 100 / NULLIF(s.start_mrr, 0) AS grr_pct
FROM start s, churn_mrr c, contraction co;
Закрепи формулу gross revenue retention в Карьернике
Запомнить надолго — 5 коротких сессий с задачами на эту тему. Бесплатно
Тренировать gross revenue retention в Telegram

По cohorts

WITH cohort_start AS (
    SELECT
        DATE_TRUNC('month', first_paid_at) AS cohort,
        user_id,
        monthly_mrr AS start_mrr
    FROM customers
    WHERE first_paid_at >= '2025-06-01' AND first_paid_at < '2025-07-01'
),
cohort_now AS (
    SELECT
        cs.user_id,
        cs.cohort,
        cs.start_mrr,
        COALESCE(c.monthly_mrr, 0) AS current_mrr,
        CASE WHEN c.churned_at IS NOT NULL THEN cs.start_mrr ELSE 0 END AS churned_mrr,
        GREATEST(cs.start_mrr - COALESCE(c.monthly_mrr, 0), 0) - CASE WHEN c.churned_at IS NOT NULL THEN cs.start_mrr ELSE 0 END AS contraction_mrr
    FROM cohort_start cs
    LEFT JOIN customers c USING (user_id)
)
SELECT
    cohort,
    SUM(start_mrr) AS starting_mrr,
    SUM(churned_mrr) AS churn_mrr,
    SUM(contraction_mrr) AS contraction_mrr,
    (SUM(start_mrr) - SUM(churned_mrr) - SUM(contraction_mrr)) * 100.0 / SUM(start_mrr) AS grr_pct
FROM cohort_now
GROUP BY cohort;

Trend GRR

WITH monthly AS (
    SELECT
        date_month,
        starting_mrr,
        churn_mrr,
        contraction_mrr,
        (starting_mrr - churn_mrr - contraction_mrr) * 100.0 / NULLIF(starting_mrr, 0) AS grr
    FROM mrr_snapshot
    WHERE date_month >= CURRENT_DATE - INTERVAL '12 months'
)
SELECT
    date_month,
    grr,
    AVG(grr) OVER (ORDER BY date_month ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS grr_3m_avg
FROM monthly
ORDER BY date_month;

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

Ошибка 1. GRR > 100%. Mathematically impossible. Bug somewhere (likely включил expansion).

Ошибка 2. Window inconsistent. Monthly GRR ≠ annualized. Define one.

Ошибка 3. Cohort vs period. Cohort GRR — single cohort over time. Period GRR — all customers in given month.

Ошибка 4. Contraction definition. Plan tier down vs seat count down vs discount applied. Define.

Ошибка 5. Pause / freeze counted as churn. Customer paused subscription. Counts? Treat consistently.

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

FAQ

Какой GRR ok?

90% — excellent. 85-90% — good. < 80% — concern.

GRR vs NRR — какая важнее?

GRR — pure retention. NRR — overall growth including expansion. Both critical.

GRR падает — что делать?

Investigate churn reasons. Cohort analysis. Customer success investment.

Annualized GRR?

Monthly GRR^12. Or annual GRR = (start - churn - contraction) / start across 12-month window.

Pause/freeze handling?

Pause-then-resume = not churn. Pause permanent — same as churn.