Churn hazard by tenure
WITH RECURSIVE months(n) AS (
SELECT 1 UNION ALL SELECT n + 1 FROM months WHERE n < 12
),
activation AS (
SELECT s.id, e.at AS t0, s.canceled_at
FROM subscriptions s
JOIN subscription_events e ON e.subscription_id = s.id AND e.kind = 'activate'
WHERE s.billing = 'monthly'
),
bounds AS (SELECT datetime(MAX(at)) AS data_end FROM subscription_events),
exposure AS (
SELECT m.n AS tenure_month,
CASE WHEN datetime(a.t0, '+' || m.n || ' months') <= b.data_end
AND (a.canceled_at IS NULL OR datetime(a.canceled_at) >= datetime(a.t0, '+' || (m.n - 1) || ' months'))
THEN 1 ELSE 0 END AS at_risk,
CASE WHEN datetime(a.canceled_at) >= datetime(a.t0, '+' || (m.n - 1) || ' months')
AND datetime(a.canceled_at) < datetime(a.t0, '+' || m.n || ' months')
THEN 1 ELSE 0 END AS canceled
FROM months m CROSS JOIN activation a CROSS JOIN bounds b
)
SELECT tenure_month,
SUM(at_risk) AS subscriptions_at_risk,
SUM(canceled) AS cancels,
ROUND(100.0 * SUM(canceled) / SUM(at_risk), 2) AS hazard_pct
FROM exposure
GROUP BY tenure_month
ORDER BY tenure_month;View data table
| tenure_month | subscriptions_at_risk | cancels | hazard_pct |
|---|---|---|---|
| 1 | 1062 | 12 | 1.13 |
| 2 | 1025 | 108 | 10.54 |
| 3 | 899 | 79 | 8.79 |
| 4 | 799 | 41 | 5.13 |
| 5 | 741 | 22 | 2.97 |
| 6 | 694 | 24 | 3.46 |
| 7 | 657 | 16 | 2.44 |
| 8 | 623 | 20 | 3.21 |
| 9 | 598 | 23 | 3.85 |
| 10 | 562 | 12 | 2.14 |
| 11 | 533 | 17 | 3.19 |
| 12 | 495 | 16 | 3.23 |
holds on the current window