What You'll Master Here
Find the breaks. Number the stretches between them. Aggregate the stretches. Every technique in this chapter is those three steps in a different order.
Rows arrive one at a time; people experience them in stretches. A visit is a run of clicks close together, a streak is a run of days without a gap, an outage is a run of failed checks. None of those is a column in any table — they all have to be derived.
This chapter covers the family of techniques that turn a row-per-event table into a row-per-episode one: gaps and islands, sessionisation, funnels, and cohort retention.
The unifying idea is small. Look at the distance between neighbouring rows, decide where that distance counts as a break, and give everything between two breaks the same group id. Everything else is aggregation.
Find the breaks. Number the stretches between them. Aggregate the stretches. Every technique in this chapter is those three steps in a different order.
Session counts, streaks, funnel conversion and retention curves are the four numbers a product team actually watches, and every one of them is this shape. They are also the questions most likely to appear in a senior SQL interview, because they cannot be answered with a GROUP BY.
- island
- A run of consecutive rows with no break — consecutive days, consecutive seat numbers, back-to-back events.
- gap
- The break between two islands. Defining what counts as a gap is the modelling decision the whole query rests on.
- session
- An island in an event stream, where the break is a period of inactivity — the classic threshold is 30 minutes.
- funnel
- An ordered sequence of steps, counted by how many entities reached each one. Order and time window are both part of the definition.
Treating "sessions" or "streaks" as something the data already contains. You look for a column that does not exist, or invent one upstream that then has to be maintained and backfilled.
State what counts as a break before writing any SQL.
Deduplicate to the grain you are measuring before numbering anything.
Check one entity by hand against the output — these queries are hard to eyeball in aggregate.
Asked for "the longest streak" or "sessionise these events", say the threshold out loud first: "a gap of more than 30 minutes starts a new session — is that the definition you want?" Half the mark is knowing that the threshold is a decision rather than a constant.
Episodes are derived, never stored. The definition of a break is yours to state.
