First Half of Page Views
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
- L4
- Asked in
- Bosch
Problem
Which page views make up the first half of our page_views log by view ID, where half is measured against however many records the table holds in total? We want every detail of each of those views plus its position number, in view ID order.
Half the data. The first half.
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
| view_id | page_url | user_id | referrer | dur_ms | device | viewed_at | rn |
|---|---|---|---|---|---|---|---|
| 5053 | /products | 1555 | amazon.com | 637 | desktop | 2026-09-02 01:09:00 | 1 |
| 5106 | /checkout | 1652 | twitter.com | 774 | tablet | 2026-09-03 02:18:00 | 2 |
| 5159 | /profile | 1749 | github.com | 911 | Mobile | 2026-09-04 03:27:00 | 3 |
| 5212 | /search | 1846 | NULL | 1048 | DESKTOP | 2026-09-05 04:36:00 | 4 |
| 5265 | /about | 1943 | reddit.com | 1185 | mobile | 2026-09-06 05:45:00 | 5 |
Worked solution and explanation
What this problem really is
This is a positional cut dressed up as a log export. The skill: number every view in view_id order and cut at a boundary the data itself supplies, half of whatever page_views holds right now. Anyone can sort by view_id. The trap is writing LIMIT 100 because the table happens to hold 200 rows, or filtering view_id <= something as if ids were contiguous. Do the first and you return no rn column and a cutoff that is wrong the day the table grows. Do the second and you grab the wrong rows outright: ids here step by 53 and later batches jump past ten million.
Break down the requirements
Step 1: Establish a deterministic position
ROW_NUMBER() OVER (ORDER BY view_id) numbers every row globally. Plain LIMIT n/2 is illegal in most engines when n is itself a subquery, and it would not give you a position to return alongside the other columns.
Step 2: Derive the cutoff
(SELECT COUNT(*) / 2 FROM page_views) is a scalar subquery evaluated once over the whole table. Integer division drops the middle row on an odd total, which is the implicit contract of a first half.
Step 3: Filter and re-order
WHERE rn <= cutoff keeps the lower half. The outer ORDER BY view_id is non-negotiable because the window's ORDER BY does not bind the order of the result set after a filter.
The solution
SELECT *
FROM (
SELECT *,
ROW_NUMBER() OVER (ORDER BY view_id) AS rn
FROM page_views
) sub
WHERE rn <= (SELECT COUNT(*) / 2 FROM page_views)
ORDER BY view_idCommon follow-up questions
Common follow-up questions
- If `view_id` has gaps from deleted rows, does a first half still mean what the business wants? (Probes whether you distinguish ordinal position from identifier value.)
- How would you return the median row when the total count is odd? (Checks understanding of integer division and the off-by-one in `n/2` versus `(n+1)/2`.)
- Rewrite this so the engine only scans `page_views` once. (Tests knowledge of windowed aggregates like `COUNT(*) OVER ()` to fold both passes into one.)