lesson 46 Window functions · advanced
NTILE & balanced ranking buckets
Split ordered rows into ranking buckets.
the idea
NTILE divides an ordered partition into a requested number of buckets. Bucket row counts differ by at most one, with larger buckets first. It may separate equal values; use a deterministic tie-breaker if that is acceptable.
tables in this lesson
The challenges use this data. Each column shows its type underneath; → marks a link to another table.
watch_events5 columns · 26 rows
| idinteger · key | viewer_idinteger | video_idinteger | watched_attext | watch_secondsinteger |
|---|---|---|---|---|
| 1 | 1 | 1 | 2025-01-01 10:00:00 | 300 |
| 2 | 1 | 2 | 2025-01-01 11:00:00 | 450 |
| 3 | 2 | 1 | 2025-01-02 12:00:00 | 600 |
| 4 | 3 | 3 | 2025-01-02 13:00:00 | 120 |
| 5 | 1 | 4 | 2025-01-02 14:00:00 | 700 |
showing 5 of 26 rows
example
The idea in one query. Press Run to see what it returns; running examples never affects your progress.
SELECT id, watch_seconds, NTILE(4) OVER( ORDER BY watch_seconds DESC,id) AS bucket FROM watch_events;
Watch for: NTILE creates roughly equal row counts, not equal numeric ranges or tie-preserving ranks.
build it step by step
Each step changes one thing. Run them in order and watch the result change.
Sort the values you will bucket. NTILE splits an ordered list into roughly equal buckets. Sort watch_seconds so bucket 1 holds the longest watches.
step-1.sqlSELECT id, watch_seconds FROM watch_events ORDER BY watch_seconds DESC, id LIMIT 12;
Assign quartile buckets. NTILE(4) labels rows 1–4 over the ordered window. Bucket edges can differ slightly when counts do not divide evenly.
step-2.sqlSELECT id, watch_seconds, NTILE(4) OVER(ORDER BY watch_seconds DESC, id) AS bucket FROM watch_events;