All examples for Business Question to SQL Translator

Monthly Churn Rate by Subscription Tier

Calculates monthly churn rate segmented by subscription tier while excluding trial users who never activated. Demonstrates complex filtering logic, time series generation with generate_series, and proper churn rate formula (churned/active at month start).

Input

What you provide to the skill

Monthly churn rate by subscription tier, exclude trial users who never activated

Output

What the skill generates for you

Decomposition

  • Core metric: Monthly churn rate (percentage of users who churned)
  • Dimensions: Subscription tier
  • Time window: Monthly (calculate for each month)
  • Edge cases: Exclude trial users who never activated (no activation event or flag)

Data Needs

Data Type Specific Fields
Entity IDs user_id, subscription_id
Timestamps month_start_date, subscription_start_date, subscription_end_date (or churn_date)
Categorical subscription_tier (e.g., basic, pro, enterprise), user_status
Boolean/Flags is_activated, is_trial, is_churned
Derived churned_in_month (boolean), active_at_month_start (boolean)

Assumed Schema

users: user_id, signup_date, is_activated (boolean)
subscriptions: subscription_id, user_id, tier, start_date, end_date (NULL if active), is_trial (boolean)

Assumptions:

  • is_activated = TRUE means the user completed activation (not just signed up for trial)
  • end_date IS NOT NULL indicates a churned subscription
  • Trial users who never activated have is_activated = FALSE
  • One subscription per user (or we consider the most recent)

Join Logic

  • INNER JOIN between subscriptions and users to ensure we only analyze users with valid activation status
  • Filter out trial users who never activated using WHERE users.is_activated = TRUE

Filters and Conditions

Key filters:

  • Exclude trial users: is_trial = FALSE OR (is_trial = TRUE AND users.is_activated = TRUE)
  • Time-based: Generate monthly cohorts
  • Churn definition: Subscription ended during the month
  • Active at start: Subscription was active at the beginning of the month

Churn rate formula:

Churn Rate = (Users who churned in month) / (Users active at start of month) * 100

SQL Query

WITH monthly_periods AS (
-- Generate list of months to analyze
SELECT
DATE_TRUNC('month', CURRENT_DATE - INTERVAL '12 months') + (n || ' months')::INTERVAL AS month_start
FROM generate_series(0, 12) AS n
),
activated_users AS (
-- Filter to only users who activated (exclude trial-only users)
SELECT
u.user_id,
u.is_activated
FROM users u
WHERE u.is_activated = TRUE
),
subscription_status AS (
-- Get subscription tier and status for activated users
SELECT
s.user_id,
s.tier AS subscription_tier,
s.start_date,
s.end_date,
mp.month_start,
mp.month_start + INTERVAL '1 month' AS month_end,
-- User was active at start of month if subscription started before month_end AND (no end_date OR ended after month_start)
CASE
WHEN s.start_date < mp.month_start + INTERVAL '1 month'
AND (s.end_date IS NULL OR s.end_date >= mp.month_start)
THEN TRUE
ELSE FALSE
END AS active_at_month_start,
-- User churned during month if end_date falls within the month
CASE
WHEN s.end_date >= mp.month_start
AND s.end_date < mp.month_start + INTERVAL '1 month'
THEN TRUE
ELSE FALSE
END AS churned_in_month
FROM subscriptions s
INNER JOIN activated_users au ON s.user_id = au.user_id
CROSS JOIN monthly_periods mp
WHERE
s.start_date < mp.month_start + INTERVAL '1 month' -- Started before month ended
AND (s.is_trial = FALSE OR au.is_activated = TRUE) -- Exclude unactivated trials
),
monthly_churn_metrics AS (
SELECT
month_start,
subscription_tier,
COUNT(DISTINCT CASE WHEN active_at_month_start THEN user_id END) AS active_users_start,
COUNT(DISTINCT CASE WHEN churned_in_month THEN user_id END) AS churned_users
FROM subscription_status
GROUP BY month_start, subscription_tier
)
SELECT
TO_CHAR(month_start, 'YYYY-MM') AS month,
subscription_tier,
active_users_start,
churned_users,
ROUND(
CASE
WHEN active_users_start > 0
THEN (100.0 * churned_users / active_users_start)
ELSE 0
END,
2
) AS churn_rate_percent
FROM monthly_churn_metrics
WHERE active_users_start > 0 -- Only show months with active users
ORDER BY month DESC, subscription_tier;

Interview Talking Points

High-Level Approach (30 seconds)

“I’ll calculate monthly churn rate by subscription tier in four steps using CTEs. First, generate a series of monthly periods. Second, identify activated users to exclude trial-only users. Third, determine each user’s status for each month - were they active at the start, did they churn during the month. Fourth, aggregate by month and tier to calculate churn rates.”

Key Design Decisions

  1. Activation filter: Used INNER JOIN with activated_users CTE to ensure we only analyze users who completed activation, explicitly excluding trial users who signed up but never activated.

  2. Churn definition: Defined churn as subscription end_date falling within the month period, using date range comparisons.

  3. Active at month start: A user is active if their subscription started before the month ended AND either has no end_date (still active) OR ended after the month started.

  4. CROSS JOIN for time series: Used CROSS JOIN with monthly periods to create a row for each user-month combination, making it easy to track status changes over time.

  5. Handling edge cases:

    • Division by zero handled with CASE statement
    • Only showing months with active users (WHERE active_users_start > 0)
    • Flexible trial exclusion using boolean logic

Verification Strategy

  1. Spot check: Pick a specific month and tier, manually pull user_ids who churned, verify their end_dates fall within that month
  2. Sanity check: Churn rate should be between 0-100%, active users should be >= churned users
  3. Compare totals: Sum churned_users across all tiers for a month, verify against total company churn
  4. Check activation filter: Query users table to confirm trial-only users (is_activated = FALSE) are excluded

Common Variations

Quarterly instead of monthly:

DATE_TRUNC('quarter', ...) instead of DATE_TRUNC('month', ...)

Include gross churn vs net churn:
Add new subscriptions (upgrades, new signups) to calculate net churn.

Cohort-based churn:
Group by signup month cohort instead of calendar month.

Revenue churn vs user churn:
Weight by subscription value instead of counting users.