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 users by when they started; retention asks what share came back later. Exact-day retention checks activity on one specific day (signup date + 7 for D7). Only include cohorts that are mature: their measurement day must fall before the data ends.
Dataset: Product / SaaS Analytics
The tables the challenges use, with their first 6 rows. Under each column name: the kind of value it holds, and which table it links to.
users9 columns · 11,348 rows
| user_idnumber · unique id | account_idnumber · links to accounts | full_nametext | emailtext | roletext | signup_sourcetext | referred_by_user_idnumber · links to users | created_atdate & time | deactivated_attext |
|---|---|---|---|---|---|---|---|---|
| 1 | 1 | Omar Jackson | omar.jackson@ironcapital1.example | owner | workspace_creator | NULL | 2024-01-14 18:52:14 | NULL |
| 2 | 1 | Imani Kang | imani.kang@ironcapital1.example | admin | invitation | NULL | 2024-01-24 03:43:14 | NULL |
| 3 | 1 | Chloe Mitchell | chloe.mitchell@ironcapital1.example | admin | invitation | NULL | 2024-02-02 21:34:25 | NULL |
| 4 | 1 | Jaya Carter | jaya.carter@ironcapital1.example | member | domain_join | NULL | 2024-03-31 03:39:32 | NULL |
| 5 | 1 | Zoe Turner | zoe.turner@ironcapital1.example | member | domain_join | NULL | 2024-02-12 02:53:31 | NULL |
| 6 | 2 | Esther Smith | esther.smith@vectordata2.example | owner | workspace_creator | NULL | 2024-01-15 13:21:52 | NULL |
- user_idnumber · unique id
- 1
- account_idnumber · links to accounts
- 1
- full_nametext
- Omar Jackson
- emailtext
- omar.jackson@ironcapital1.example
- roletext
- owner
- signup_sourcetext
- workspace_creator
- referred_by_user_idnumber · links to users
- NULL
- created_atdate & time
- 2024-01-14 18:52:14
- deactivated_attext
- NULL
- user_idnumber · unique id
- 2
- account_idnumber · links to accounts
- 1
- full_nametext
- Imani Kang
- emailtext
- imani.kang@ironcapital1.example
- roletext
- admin
- signup_sourcetext
- invitation
- referred_by_user_idnumber · links to users
- NULL
- created_atdate & time
- 2024-01-24 03:43:14
- deactivated_attext
- NULL
- user_idnumber · unique id
- 3
- account_idnumber · links to accounts
- 1
- full_nametext
- Chloe Mitchell
- emailtext
- chloe.mitchell@ironcapital1.example
- roletext
- admin
- signup_sourcetext
- invitation
- referred_by_user_idnumber · links to users
- NULL
- created_atdate & time
- 2024-02-02 21:34:25
- deactivated_attext
- NULL
- user_idnumber · unique id
- 4
- account_idnumber · links to accounts
- 1
- full_nametext
- Jaya Carter
- emailtext
- jaya.carter@ironcapital1.example
- roletext
- member
- signup_sourcetext
- domain_join
- referred_by_user_idnumber · links to users
- NULL
- created_atdate & time
- 2024-03-31 03:39:32
- deactivated_attext
- NULL
- user_idnumber · unique id
- 5
- account_idnumber · links to accounts
- 1
- full_nametext
- Zoe Turner
- emailtext
- zoe.turner@ironcapital1.example
- roletext
- member
- signup_sourcetext
- domain_join
- referred_by_user_idnumber · links to users
- NULL
- created_atdate & time
- 2024-02-12 02:53:31
- deactivated_attext
- NULL
- user_idnumber · unique id
- 6
- account_idnumber · links to accounts
- 2
- full_nametext
- Esther Smith
- emailtext
- esther.smith@vectordata2.example
- roletext
- owner
- signup_sourcetext
- workspace_creator
- referred_by_user_idnumber · links to users
- NULL
- created_atdate & time
- 2024-01-15 13:21:52
- deactivated_attext
- NULL
first 6 of 11,348 rows · your queries use all of them
events8 columns · 135,428 rows
| delivery_idnumber · unique id | event_idtext | user_idnumber · links to users | account_idnumber · links to accounts | session_idnumber · links to sessions | event_nametext | occurred_atdate & time | received_atdate & time |
|---|---|---|---|---|---|---|---|
| 1 | evt_0000001 | 1 | 1 | 1 | user_signed_up | 2024-01-14 18:52:14 | 2024-01-14 18:52:33 |
| 2 | evt_0000002 | 1 | 1 | 1 | onboarding_started | 2024-01-14 18:52:51 | 2024-01-14 18:53:06 |
| 3 | evt_0000084 | 6 | 2 | 32 | user_signed_up | 2024-01-15 13:21:52 | 2024-01-15 13:22:00 |
| 4 | evt_0000085 | 6 | 2 | 32 | onboarding_started | 2024-01-15 13:22:05 | 2024-01-15 13:22:16 |
| 5 | evt_0000086 | 6 | 2 | 32 | onboarding_completed | 2024-01-15 13:23:52 | 2024-01-15 13:24:12 |
| 6 | evt_0000019 | 1 | 1 | 9 | search_performed | 2024-01-17 20:56:55 | 2024-01-17 20:56:59 |
- delivery_idnumber · unique id
- 1
- event_idtext
- evt_0000001
- user_idnumber · links to users
- 1
- account_idnumber · links to accounts
- 1
- session_idnumber · links to sessions
- 1
- event_nametext
- user_signed_up
- occurred_atdate & time
- 2024-01-14 18:52:14
- received_atdate & time
- 2024-01-14 18:52:33
- delivery_idnumber · unique id
- 2
- event_idtext
- evt_0000002
- user_idnumber · links to users
- 1
- account_idnumber · links to accounts
- 1
- session_idnumber · links to sessions
- 1
- event_nametext
- onboarding_started
- occurred_atdate & time
- 2024-01-14 18:52:51
- received_atdate & time
- 2024-01-14 18:53:06
- delivery_idnumber · unique id
- 3
- event_idtext
- evt_0000084
- user_idnumber · links to users
- 6
- account_idnumber · links to accounts
- 2
- session_idnumber · links to sessions
- 32
- event_nametext
- user_signed_up
- occurred_atdate & time
- 2024-01-15 13:21:52
- received_atdate & time
- 2024-01-15 13:22:00
- delivery_idnumber · unique id
- 4
- event_idtext
- evt_0000085
- user_idnumber · links to users
- 6
- account_idnumber · links to accounts
- 2
- session_idnumber · links to sessions
- 32
- event_nametext
- onboarding_started
- occurred_atdate & time
- 2024-01-15 13:22:05
- received_atdate & time
- 2024-01-15 13:22:16
- delivery_idnumber · unique id
- 5
- event_idtext
- evt_0000086
- user_idnumber · links to users
- 6
- account_idnumber · links to accounts
- 2
- session_idnumber · links to sessions
- 32
- event_nametext
- onboarding_completed
- occurred_atdate & time
- 2024-01-15 13:23:52
- received_atdate & time
- 2024-01-15 13:24:12
- delivery_idnumber · unique id
- 6
- event_idtext
- evt_0000019
- user_idnumber · links to users
- 1
- account_idnumber · links to accounts
- 1
- session_idnumber · links to sessions
- 9
- event_nametext
- search_performed
- occurred_atdate & time
- 2024-01-17 20:56:55
- received_atdate & time
- 2024-01-17 20:56:59
first 6 of 135,428 rows · your queries use all of them
worked example
Run these steps in order to see the idea before you write your own query. The step marked “the pattern” is what the challenges ask for. Running the example does not count toward progress.
Size each signup month. Run this. cohort_month groups users by when they joined—the denominator before you measure who came back.the pattern
example.sqlSELECT substr(created_at, 1, 7) AS cohort_month, COUNT(*) AS signups FROM users GROUP BY cohort_month;
Check one user on day 7. Run this. d7 is signup plus seven days; events_on_d7 counts activity on that calendar day only.
step-2.sqlSELECT u.user_id, date(u.created_at) AS signup_day, date(u.created_at, '+7 days') AS d7, COUNT(e.delivery_id) AS events_on_d7 FROM users u LEFT JOIN events e ON e.user_id = u.user_id AND date(e.occurred_at) = date(u.created_at, '+7 days') WHERE u.user_id BETWEEN 9000 AND 9010 GROUP BY u.user_id;
See who is D7-measurable. Run this. d7_observable counts signups whose seventh day already passed in the data—recent signups stay out until you can see that day.
step-3.sqlSELECT substr(created_at, 1, 7) AS cohort_month, COUNT(*) AS signups, SUM(date(created_at, '+7 days') <= '2025-06-30') AS d7_observable FROM users WHERE created_at >= '2025-05-01' GROUP BY cohort_month;
common mistake
Users who signed up on 2025-06-28 cannot have D7 activity in data that ends on 2025-06-30. Including them makes the newest cohort look worse.