lesson 50 Advanced product analysis · advanced
Deduplicate event retries
Identify repeated events and keep a consistent record.
the idea
Clients retry when a delivery times out, so one logical event can arrive several times with the same event_id. Keep one delivery per event_id with ROW_NUMBER() OVER (PARTITION BY event_id ORDER BY received_at, delivery_id) = 1, then count logical events.
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.
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.
Find event_ids with retries. Run this. deliveries > 1 means the pipeline delivered the same logical event more than once.the pattern
example.sqlSELECT event_id, COUNT(*) AS deliveries FROM events GROUP BY event_id HAVING COUNT(*) > 1 LIMIT 10;
Open one retried event. Run this. You see every delivery row for a single event_id—same occurred_at, different delivery_id and received_at.
step-2.sqlSELECT delivery_id, event_id, event_name, occurred_at, received_at FROM events WHERE event_id = ( SELECT event_id FROM events GROUP BY event_id HAVING COUNT(*) > 1 LIMIT 1 );
Rank deliveries per event. Run this. ROW_NUMBER() ordered by received_at marks the earliest delivery as 1—that is the copy you keep for analytics.
step-3.sqlSELECT event_id, delivery_id, received_at, ROW_NUMBER() OVER (PARTITION BY event_id ORDER BY received_at, delivery_id) AS delivery_rank FROM events WHERE event_id IN ( SELECT event_id FROM events WHERE occurred_at >= '2025-05-01' AND occurred_at < '2025-05-02' GROUP BY event_id HAVING COUNT(*) > 1 ) ORDER BY event_id, delivery_rank;
common mistake
COUNT(*) on the raw table counts deliveries, not events. Retries inflate every metric built on it.