Top Lessons Each Month
A medium SQL interview practice problem on DataDriven. Write the query and run it against a real database, with instant feedback.
- Domain
- SQL
- Difficulty
- medium
- Seniority
- L5
- Asked in
- Walmart
Problem
Walking the calendar from the earliest month forward, surface the three most-completed lessons in each month, most completions first, and let the page address break the tie alphabetically whenever two lessons finished level. The content team uses this to guide investment decisions for the next quarter.
Rank items within time periods and keep top 3
Tables
page_views
Columns: view_id INTEGER, page_url TEXT, user_id INTEGER, referrer TEXT, dur_ms INTEGER, device TEXT, viewed_at TEXT
| view_id | page_url | user_id | referrer | dur_ms | device | viewed_at |
|---|---|---|---|---|---|---|
| 5053 | /products | 1555 | amazon.com | 637 | desktop | 2026-09-02 01:09:00 |
| 5106 | /checkout | 1652 | twitter.com | 774 | tablet | 2026-09-03 02:18:00 |
| 5159 | /profile | 1749 | github.com | 911 | Mobile | 2026-09-04 03:27:00 |
| 5212 | /search | 1846 | NULL | 1048 | DESKTOP | 2026-09-05 04:36:00 |
| 5265 | /about | 1943 | reddit.com | 1185 | mobile | 2026-09-06 05:45:00 |
| 5318 | /blog/intro | 1555 | NULL | NULL | desktop | 2026-09-07 06:54:00 |
| 5371 | /pricing | 1652 | bing.com | 1459 | tablet | 2026-09-08 07:03:00 |
| 5424 | /docs/api | 1749 | news.ycombinator.com | 1596 | Mobile | 2026-09-09 08:12:00 |
| 5477 | /settings | 1846 | NULL | 1733 | DESKTOP | 2026-09-10 09:21:00 |
| 5530 | /Home | 1943 | bing.com | 1870 | mobile | 2026-09-11 10:30:00 |
Expected output
| month | page_url | completion_count | rank |
|---|---|---|---|
| 2022-01 | /about | 1 | 1 |
| 2022-01 | /products | 1 | 1 |
| 2022-02 | /checkout | 1 | 1 |
| 2022-02 | new_editor | 1 | 1 |
| 2026-01 | /checkout | 2 | 1 |
Worked solution and explanation
Why this problem exists in real interviews
Notice the gap: the prompt asks about lesson completions, the table is page_views. A row here is a view, not a completion. In a real review you would push for a lesson_completions event source. For this exercise, page_url stands in as the lesson identifier and every row counts as a completion, which is the simplification the query bakes in.
Break down the requirements
Step 1: Bucket views into months
strftime('%Y-%m', viewed_at) returns a sortable text label per month. Aggregating to (month, page_url) collapses 500M rows into a small set keyed by lesson and month.
Step 2: Rank within each month
RANK() OVER (PARTITION BY month ORDER BY completion_count DESC) resets per month. Two lessons tied for second both get rank 2; rank 3 still exists. The next position is 4, not 3.
Step 3: Filter to the top three and order for the consumer
WHERE rnk <= 3 keeps the top tier. The outer ORDER BY month, rnk produces the calendar walk the content team expects to read.
The solution
WITH monthly AS (
SELECT strftime('%Y-%m', viewed_at) AS month,
page_url,
COUNT(*) AS completion_count
FROM page_views
GROUP BY 1, 2
),
ranked AS (
SELECT month,
page_url,
completion_count,
RANK() OVER (PARTITION BY month ORDER BY completion_count DESC) AS rnk
FROM monthly
)
SELECT month, page_url, completion_count, rnk AS rank
FROM ranked
WHERE rnk <= 3
ORDER BY month, rnk, page_urlCOMMON FOLLOW-UP QUESTIONS
Common follow-up questions
- How would you change the query to count only views where `dur_ms` exceeds a completion threshold? (Tests moving from a proxy metric to a defensible engagement filter.)
- If the content team only cares about the last six months, what changes in the query plan? (Probes partition pruning on `viewed_at` and whether the predicate belongs in the CTE or outside.)
- How would you return exactly three rows per month even on ties? (Forces the `RANK` vs `ROW_NUMBER` tradeoff and a discussion of fair tiebreakers.)