Top Buyers of Premium Products
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
Problem
We run an online marketplace and want to see which categories draw the biggest crowd of premium shoppers, treating a product as premium once its rating reaches 4.0. For each category, count how many different shoppers bought one of its premium products, then give the categories a standing by that crowd, the biggest first.
Which users bought the most top-rated products
Tables
products
Columns: product_id INTEGER, product_name TEXT, category TEXT, price REAL, rating REAL, in_stock INTEGER
| product_id | product_name | category | price | rating | in_stock |
|---|---|---|---|---|---|
| 1001 | Basic Device 1X | Books | 17.52 | 2.7 | 1 |
| 1050 | Deluxe Bundle 2X | Clothing | 25.05 | 4.4 | 1 |
| 1099 | Pro Unit 3X | Home & Kitchen | 32.58 | 2.1 | 1 |
| 1148 | Ultra Tool 4X | Collectibles | 40.11 | 3.8 | 1 |
| 1197 | Essential Set 5X | Toys | 47.64 | 1.5 | 1 |
| 1246 | Classic System 6X | Beauty | 0 | 3.2 | 1 |
| 1295 | Smart Gadget 7X | Automotive | 62.7 | 4.9 | 0 |
| 1344 | Eco Kit 8X | Garden | 70.23 | 2.6 | 1 |
| 1393 | Turbo Pack 9X | Music | 77.76 | 4.3 | 0 |
| 1442 | Premium Widget 10X | Electronics | 85.29 | 2 | 1 |
order_items
Columns: item_id INTEGER, order_id INTEGER, product_id INTEGER, user_id INTEGER, quantity INTEGER, unit_price REAL
| item_id | order_id | product_id | user_id | quantity | unit_price |
|---|---|---|---|---|---|
| 6061 | 10388 | 1344 | 1167 | 4 | 22.3 |
| 6122 | 10679 | 1687 | 2234 | 7 | 39.6 |
| 6183 | 10970 | 2030 | 3301 | 10 | 56.9 |
| 6244 | 11261 | 2373 | 4368 | 3 | 74.2 |
| 6305 | 11552 | 2716 | 5435 | 6 | 91.5 |
| 6366 | 11843 | 3059 | 6502 | 9 | 108.8 |
| 6427 | 12134 | 3402 | 7569 | 2 | 126.1 |
| 6488 | 12425 | 3745 | 8636 | 5 | 143.4 |
| 6549 | 12716 | 4088 | 9703 | 8 | 160.7 |
| 6610 | 13007 | 4431 | 1070 | 1 | 178 |
Expected output
| category | buyer_count | rank_pos |
|---|---|---|
| Automotive | 3 | 1 |
| Beauty | 3 | 1 |
| Books | 2 | 2 |
| Electronics | 2 | 2 |
| Collectibles | 1 | 3 |
Worked solution and explanation
What this problem is really testing
This is a per-category distinct-buyer count wearing a merchandising costume. The real skill: filter products to the premium set, join to order_items, and count the distinct people behind the purchases, not the rows. Every buyer here sits on two line items per product, so COUNT(*) reports twice the true audience and every category looks artificially large. Then you have to position the categories so equals share a place, which is where DENSE_RANK separates from RANK and ROW_NUMBER.
- products: product_id INTEGER PK, category TEXT, rating REAL
- order_items: item_id INTEGER PK, product_id INTEGER FK, user_id INTEGER
- order_items references products
Walk through it
Step 1: Isolate the premium products
In a premium_products CTE, keep only rows where rating >= 4.0. That comparison also drops the NULL-rated products for free (a NULL rating is never >= 4.0), so the join downstream only ever sees genuinely premium goods.
Step 2: Join to purchases and count real buyers
INNER JOIN order_items on product_id, group by category, and take COUNT(DISTINCT oi.user_id). The DISTINCT is the whole game: each buyer appears on multiple line items per product, so COUNT(*) would report the crowd at double size.
Step 3: Award a shared standing
Over that per-category buyer_count, apply DENSE_RANK() OVER (ORDER BY buyer_count DESC). Categories with the same crowd get the same rank_pos, and the next tier down is the very next number, no gap.
Step 4: Order the leaderboard
Finish with ORDER BY buyer_count DESC, category. The category tiebreak makes the order total, so two runs never disagree on how equals are laid out.
The solution
WITH premium_products AS (
SELECT product_id, category
FROM products
WHERE rating >= 4.0
),
category_buyers AS (
SELECT
pp.category,
COUNT(DISTINCT oi.user_id) AS buyer_count
FROM premium_products pp
INNER JOIN order_items oi
ON oi.product_id = pp.product_id
GROUP BY pp.category
)
SELECT
category,
buyer_count,
DENSE_RANK() OVER (ORDER BY buyer_count DESC) AS rank_pos
FROM category_buyers
ORDER BY buyer_count DESC, categoryFilter, then distinct-count, then a gap-free standing.
Common follow-up questions
Common follow-up questions
- Count distinct premium products bought per category instead of distinct buyers. (Swap `COUNT(DISTINCT oi.user_id)` for `COUNT(DISTINCT oi.product_id)` and reason about how the join grain changes the answer.)
- Only count purchases from the last 12 months. (Forces an `order_items` date predicate and a conversation about index coverage on the big table.)
- Why is `INNER JOIN` right here rather than `LEFT JOIN`? (Checks that a category with no premium purchases is absent by design, not silently zero.)