Event contract
Fields the query expects
| Field | Type | Why it exists |
|---|---|---|
| timestamp_utc | Timestamp | Billing movement time. |
| account_id | Utf8 | Stable billing account identifier. |
| movement_type | Utf8 | new, expansion, contraction, churn, or reactivation. |
| mrr_change_usd | Float64 | Signed normalized recurring-revenue change. |
Copy the query
SELECT
date_trunc('month', timestamp_utc) AS month,
SUM(CASE
WHEN movement_type IN ('new', 'expansion', 'reactivation')
THEN mrr_change_usd ELSE 0.0
END) AS growth_mrr_usd,
-SUM(CASE
WHEN movement_type IN ('contraction', 'churn')
THEN mrr_change_usd ELSE 0.0
END) AS lost_mrr_usd,
SUM(mrr_change_usd) AS net_mrr_change_usd,
COUNT(DISTINCT account_id) AS accounts_changed
FROM billing_events
WHERE timestamp_utc >= now() - INTERVAL '12 months'
GROUP BY date_trunc('month', timestamp_utc)
ORDER BY month;This read-only query is planned and executed against an empty typed table with Apache DataFusion 45.2.0. The deterministic sample output is synthetic and reviewed separately; validate field types, thresholds, and business definitions against your own data. Read the testing methodology.
Query result
Recurring-revenue growth and loss
July remained positive, but contraction and churn consumed much more of gross growth.
| month | growth_mrr_usd | lost_mrr_usd | net_mrr_change_usd | accounts_changed |
|---|---|---|---|---|
| 2026-05 | 18,400 | 7,200 | 11,200 | 93 |
| 2026-06 | 22,100 | 9,800 | 12,300 | 117 |
| 2026-07 | 20,700 | 13,400 | 7,300 | 126 |
Synthetic example output. Run the query against your own event schema and thresholds before using it for operational decisions.
How the SQL works
- 1Positive movements combine new, expansion, and reactivation MRR.
- 2Lost MRR is negated for a readable positive bar while net change retains the signed total.
- 3Distinct account count helps distinguish a broad movement from one unusually large contract.
Edge cases to decide
- Emit exactly one movement for a billing change or reconcile by provider event ID.
- Define whether annual contracts contribute normalized monthly value at booking or service time.
- Separate one-time fees and usage revenue from recurring revenue.
Recommended dashboard
- Stacked bars: growth_mrr_usd and lost_mrr_usd
- Trend: net_mrr_change_usd
- Table: largest account-level movements
Alert guidance
Review when lost MRR consumes an unusually large share of gross growth for a complete month.
Read alert setupPut the recipe to work
Related instrumentation and guides
Continue the analysis
Find Features Used Before Upgrade
Join feature events to upgrade events and rank behaviors that occur before paid conversion.
Open recipeMeasure Churn and Reactivation by Cohort
Classify accounts as retained, churned, or reactivated from recurring meaningful product activity.
Open recipeCalculate Trial-to-Paid Conversion
Measure how many trial accounts become paid customers within a fixed conversion window.
Open recipeRun it on real events
Create a table, adapt the fields, and save the result
Start free, send structured events, and use the query result as a chart, shared dashboard widget, or alert input.