48 lines
1.5 KiB
YAML
48 lines
1.5 KiB
YAML
name: churn_rate
|
|
display_name: Monthly Churn Rate
|
|
category: revenue
|
|
type: ratio
|
|
unit: percentage
|
|
grain: monthly
|
|
table: subscriptions
|
|
expression: "churned_mrr / beginning_mrr * 100"
|
|
time_column: billing_date
|
|
description: "Monthly revenue churn rate — the percentage of MRR lost due to cancellations and downgrades in a given month."
|
|
dimensions:
|
|
- plan_type
|
|
- region
|
|
synonyms:
|
|
- mrr_churn
|
|
- revenue_churn
|
|
- monthly_churn
|
|
notes:
|
|
- "Revenue churn differs from customer churn — one large customer churning can dominate"
|
|
- "Negative churn occurs when expansion revenue exceeds lost revenue"
|
|
- "Target: <2% monthly for healthy SaaS businesses"
|
|
- "Churned MRR includes full cancellations and partial downgrades"
|
|
sql: |
|
|
WITH monthly_mrr AS (
|
|
SELECT
|
|
DATE_TRUNC('month', billing_date) AS month,
|
|
SUM(CASE WHEN status = 'active' THEN mrr_amount ELSE 0 END) AS active_mrr,
|
|
SUM(CASE WHEN status = 'churned'
|
|
AND DATE_TRUNC('month', churned_at) = DATE_TRUNC('month', billing_date)
|
|
THEN mrr_amount ELSE 0 END) AS churned_mrr
|
|
FROM subscriptions
|
|
GROUP BY 1
|
|
),
|
|
lagged AS (
|
|
SELECT
|
|
month,
|
|
churned_mrr,
|
|
LAG(active_mrr) OVER (ORDER BY month) AS beginning_mrr
|
|
FROM monthly_mrr
|
|
)
|
|
SELECT
|
|
month,
|
|
churned_mrr,
|
|
beginning_mrr,
|
|
ROUND(churned_mrr / NULLIF(beginning_mrr, 0) * 100, 2) AS churn_rate_pct
|
|
FROM lagged
|
|
WHERE beginning_mrr IS NOT NULL
|
|
ORDER BY month
|