runtime
53 / 88

lesson 53 Advanced product analysis · advanced

Cohorts & exact-day retention

Measure whether users return on a specific day after joining.

the idea

A cohort groups people by signup date. Exact-day D1 retention asks whether they return on signup +1 day, not anytime later. Count people, include non-returners, and exclude cohorts that have not matured by the observation cutoff.

tables in this lesson

The challenges use this data. Each column shows its type underneath; → marks a link to another table.

viewers4 columns · 8 rows
idinteger · keycountrytextsignup_attextreferred_byinteger
1US2025-01-01NULL
2US2025-01-021
3GB2025-01-021
4IN2025-01-032
5GB2025-01-04NULL

showing 5 of 8 rows

watch_events5 columns · 26 rows
idinteger · keyviewer_idintegervideo_idintegerwatched_attextwatch_secondsinteger
1112025-01-01 10:00:00300
2122025-01-01 11:00:00450
3212025-01-02 12:00:00600
4332025-01-02 13:00:00120
5142025-01-02 14:00:00700

showing 5 of 26 rows

example

The idea in one query. Press Run to see what it returns; running examples never affects your progress.

example.sql
-- D1 retention; observation cutoff is 2025-01-15

SELECT
  v.signup_at AS cohort_day,
  COUNT(*) AS cohort_size,
  SUM(CASE
    WHEN EXISTS(
      SELECT 1
      FROM watch_events w
      WHERE w.viewer_id=v.id
        AND date(w.watched_at)=date(v.signup_at,'+1 days')
    ) THEN 1
    ELSE 0
  END) AS retained_viewers,
  ROUND(100.0*SUM(CASE
    WHEN EXISTS(
      SELECT 1
      FROM watch_events w
      WHERE w.viewer_id=v.id
        AND date(w.watched_at)=date(v.signup_at,'+1 days')
    ) THEN 1
    ELSE 0
  END)/COUNT(*),2) AS retention_pct
FROM viewers v
WHERE v.signup_at<=date('2025-01-15','-1 days')
GROUP BY v.signup_at

Watch for: A seven-day window and exact-day D7 retention are different metrics.

build it step by step

Each step changes one thing. Run them in order and watch the result change.

  1. Define mature cohorts. Each row is one viewer in a cohort that can have a D7 observation.

    step-1.sql
    SELECT id,signup_at,date(signup_at,'+7 days') AS return_day
    FROM viewers
    WHERE signup_at<=date('2025-01-15','-7 days');
  2. Mark exact-day returns. EXISTS counts each person at most once, even if several events occur on the return date.

    step-2.sql
    SELECT
      v.id,
      v.signup_at,
      CASE
        WHEN EXISTS(
          SELECT 1
          FROM watch_events w
          WHERE w.viewer_id=v.id
            AND date(w.watched_at)=date(v.signup_at,'+7 days')
        ) THEN 1
        ELSE 0
      END AS retained
    FROM viewers v
    WHERE v.signup_at<=date('2025-01-15','-7 days');
  3. Report D7 retention. Retained people divided by all mature cohort members.

    step-3.sql
    SELECT
      v.signup_at AS cohort_day,
      COUNT(*) AS cohort_size,
      SUM(CASE
        WHEN EXISTS(
          SELECT 1
          FROM watch_events w
          WHERE w.viewer_id=v.id
            AND date(w.watched_at)=date(v.signup_at,'+7 days')
        ) THEN 1
        ELSE 0
      END) AS retained_viewers,
      ROUND(100.0*SUM(CASE
        WHEN EXISTS(
          SELECT 1
          FROM watch_events w
          WHERE w.viewer_id=v.id
            AND date(w.watched_at)=date(v.signup_at,'+7 days')
        ) THEN 1
        ELSE 0
      END)/COUNT(*),2) AS retention_pct
    FROM viewers v
    WHERE v.signup_at<=date('2025-01-15','-7 days')
    GROUP BY v.signup_at

challenge 1

With observation cutoff Jan 15 2025, return cohort_day, cohort_size, retained_viewers and retention_pct (0–100, 2 decimals) for exact-day D1 retention. Include only mature cohorts.

Return cohort_daycohort_sizeretained_viewersretention_pctany row order

challenge-1.sql
 
To master this lesson, solve both challenges without hints and pass the concept check. 3 of 3 to go.