users(user_id, signup_date) and activity(user_id, activity_date)
A user is churned once they have gone 30 consecutive days with no activity. The clock starts at signup, so somebody who signs up and never returns churns 30 days later. The churn moment is the end of that 30-day silence.
For each signup cohort month and month_number 0 to 6, return how many of the
cohort had not yet churned by the last day of that relative month, and the
percentage.
Return cohort_month as 'YYYY-MM', month_number, surviving_users and
survival_pct rounded to two decimals, ordered by cohort then month number.
Unlike ordinary retention this curve can only fall. A user who comes back after churning does not un-churn, so each user needs a single churn date, which is the earliest 30-day silence rather than the last.
Tables
activity
user_id activity_date
------- -------------
1 2026-01-20
1 2026-02-10
1 2026-03-05
1 2026-04-01
1 2026-04-20
1 2026-05-10
1 2026-06-01
1 2026-06-20
... 9 more row(s)
users
user_id signup_date
------- -----------
1 2026-01-05
2 2026-01-10
3 2026-01-15
4 2026-01-20
Expected result
cohort_month month_number surviving_users survival_pct
------------ ------------ --------------- ------------
2026-01 0 4 100.00
2026-01 1 2 50.00
2026-01 2 1 25.00
2026-01 3 1 25.00
2026-01 4 1 25.00
2026-01 5 1 25.00
2026-01 6 1 25.00