Category Buyers

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

A merchandising analyst noticed the promotion shortlist keeps surfacing categories that fewer than three different customers have ever bought from. Using transactions and products, list the categories that clear that bar, with each one's number of unique buyers and total revenue, most revenue first.

Which categories have the broadest reach?

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

transactions

Columns: transaction_id INTEGER, user_id INTEGER, product_id INTEGER, quantity INTEGER, total_amount REAL, transaction_date TEXT

transactions: 10 of 200 rows
transaction_iduser_idproduct_idquantitytotal_amounttransaction_date
10671971001223.462026-02-02
11342941050336.932026-03-03
12013911099450.42026-04-04
12684881148563.872026-05-05
13355851197002026-06-06
14026821246290.812026-07-07
146977912953104.282026-08-08
153687613444117.752026-09-09
160397313935-452026-01-10
1670107014421144.692026-02-11

Expected output

First 5 of 10 rows
categoryunique_buyerstotal_revenue
Electronics315016.8
Books312501.39
Clothing312487.92
Home & Kitchen312222
Toys312090.78

Worked solution and explanation

A customer count dressed up as a sales report

Anyone can join transactions to products and sum total_amount per category. What separates candidates is counting the audience as different user_id values rather than purchase rows, then putting the at-least-three cutoff on that per-category count. On this data every category has about twenty purchases but only three buyers, so COUNT(*) reports roughly twenty everywhere, and 'Collectibles', which only two customers ever bought from, sails onto the shortlist it should miss.

Step 1: Attach a category to every sale

Inner-join transactions to products on product_id. A few sales carry a NULL product_id and cannot be placed in any category; the inner join drops them, which is right, because revenue you cannot attribute is not a category's revenue.

Step 2: Count people, not rows

COUNT(DISTINCT t.user_id) collapses a customer's repeat purchases into one buyer. Beside it, SUM(t.total_amount) adds every sale as stored, refunds and voids included, because revenue is the money that actually moved.

Step 3: Cut on the category's audience

The cutoff is a property of the category after its sales are rolled up, so it lives in HAVING COUNT(DISTINCT t.user_id) >= 3. A WHERE sees one sale at a time and has no buyer count to compare against.

Step 4: Lead with the money

ORDER BY total_revenue DESC sorts on the alias, so the revenue expression is written once and the biggest categories come first.

Unique buyers and revenue per category
SELECT
    p.category,
    COUNT(DISTINCT t.user_id) AS unique_buyers,
    SUM(t.total_amount) AS total_revenue
FROM products p
JOIN transactions t ON p.product_id = t.product_id
GROUP BY p.category
HAVING COUNT(DISTINCT t.user_id) >= 3
ORDER BY total_revenue DESC
Counting purchases
COUNT(*) or COUNT(t.user_id) counts every sale row: about twenty per category, and 'Collectibles' with four sales clears a three-buyer cutoff it should fail.
Counting customers
COUNT(DISTINCT t.user_id) counts each customer once: three per category, and 'Collectibles' stops at two and drops out.

Common follow-up questions