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

products: 10 of 200 rows
product_idproduct_namecategorypriceratingin_stock
1001Basic Device 1XBooks17.522.71
1050Deluxe Bundle 2XClothing25.054.41
1099Pro Unit 3XHome & Kitchen32.582.11
1148Ultra Tool 4XCollectibles40.113.81
1197Essential Set 5XToys47.641.51
1246Classic System 6XBeauty03.21
1295Smart Gadget 7XAutomotive62.74.90
1344Eco Kit 8XGarden70.232.61
1393Turbo Pack 9XMusic77.764.30
1442Premium Widget 10XElectronics85.2921

order_items

Columns: item_id INTEGER, order_id INTEGER, product_id INTEGER, user_id INTEGER, quantity INTEGER, unit_price REAL

order_items: 10 of 200 rows
item_idorder_idproduct_iduser_idquantityunit_price
60611038813441167422.3
61221067916872234739.6
618310970203033011056.9
62441126123734368374.2
63051155227165435691.5
636611843305965029108.8
642712134340275692126.1
648812425374586365143.4
654912716408897038160.7
661013007443110701178

Expected output

First 5 of 11 rows
categorybuyer_countrank_pos
Automotive31
Beauty31
Books22
Electronics22
Collectibles13

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.

`order_items.product_id` joins to `products.product_id`; `rating` gates the premium set, `user_id` is the buyer.
  • 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

Premium crowd by category
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, category

Filter, then distinct-count, then a gap-free standing.


Common follow-up questions

Common follow-up questions