View Count Per Page

An easy SQL interview practice problem on DataDriven. Write the query and run it against a real database, with instant feedback.

Domain
SQL
Difficulty
easy
Seniority
L3
Asked in
Meta

Problem

Each row in page_views is one page view, and the logger is inconsistent about letter case, so '/Home' and '/home' are the same page recorded two ways. Show each page URL in lowercase with how many times it was viewed, most viewed first.

Every page has visitors. Some just have more.

Tables

page_views

Columns: view_id INTEGER, page_url TEXT, user_id INTEGER, referrer TEXT, dur_ms INTEGER, device TEXT, viewed_at TEXT

page_views: 10 of 200 rows
view_idpage_urluser_idreferrerdur_msdeviceviewed_at
5053/products1555amazon.com637desktop2026-09-02 01:09:00
5106/checkout1652twitter.com774tablet2026-09-03 02:18:00
5159/profile1749github.com911Mobile2026-09-04 03:27:00
5212/search1846NULL1048DESKTOP2026-09-05 04:36:00
5265/about1943reddit.com1185mobile2026-09-06 05:45:00
5318/blog/intro1555NULLNULLdesktop2026-09-07 06:54:00
5371/pricing1652bing.com1459tablet2026-09-08 07:03:00
5424/docs/api1749news.ycombinator.com1596Mobile2026-09-09 08:12:00
5477/settings1846NULL1733DESKTOP2026-09-10 09:21:00
5530/Home1943bing.com1870mobile2026-09-11 10:30:00

Expected output

First 5 of 12 rows
page_urlview_count
/products30
/home28
/checkout16
new_editor14
classic_editor14

Worked solution and explanation

What this really is

This is a row count per key, dressed up as content analytics, with two ways to get it quietly wrong. The first is reading 'how many times it was viewed' as 'how many people viewed it' and reaching for COUNT(DISTINCT user_id). In page_views only 17 users generate all the traffic, so unique viewers flatten every page to a handful and the ranking collapses. The second is grouping on the raw page_url. '/Home' and '/home' are one page logged two ways, so leaving the case alone splits each into two half-sized rows. The content team then sees two middling pages where one page is actually the busiest on the site.

Step 1: Decide what one row means

Each row in page_views is a single view event. 'How many times it was viewed' is therefore a count of rows, COUNT(*), not a count of user_id values. COUNT(user_id) is wrong too: it skips the 26 views with a NULL user, and an anonymous view is still a view.

Step 2: Normalize the key before collapsing

Group on LOWER(page_url), not page_url. The seed holds '/PRODUCTS' beside '/products' and '/Home' beside '/home'. Lowercasing in the GROUP BY merges each pair into one bucket, and returning the same LOWER(page_url) in the SELECT keeps the output key consistent with the grouping.

Step 3: Sort by the count

ORDER BY view_count DESC puts the busiest pages first. Pages with the same count may come back in any order: nothing in the question ranks one tied page above another, so no tiebreaker is owed.

Views per page, case-normalized, busiest first
SELECT LOWER(page_url) AS page_url, COUNT(*) AS view_count
FROM page_views
GROUP BY LOWER(page_url)
ORDER BY view_count DESC
Raw key
GROUP BY page_url returns 14 rows: '/products' at 16 and '/PRODUCTS' at 14 sit far apart, and '/home' and '/Home' tie with every minor page at 14.
Normalized key
GROUP BY LOWER(page_url) returns 12 rows: '/products' at 30 and '/home' at 28 lead clearly, which is the answer the team actually needs.

Common follow-up questions