Shared Category Purchasers

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
Meta

Problem

The recommendations team is building a collaborative filtering feature and wants co-purchasers who shop the same aisle. For each product category, take every pair of users who both bought in that category and count the ways one user's purchases in that category combine with the other's, reporting each pair only once with the smaller of the two user IDs as the pair's first member.

They bought different things from the same aisle.

Tables

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

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

Expected output

First 5 of 31 rows
categoryuser1user2shared_categories
Automotive29477924
Automotive294126418
Automotive779126448
Beauty19768230
Beauty197116730

Worked solution and explanation

Why this problem exists in real interviews

This is a per-category pairwise co-occurrence dressed up as a recommender feature. The real skill is joining transactions to itself so two different buyers meet in one row, then canonicalizing each unordered pair with t1.user_id < t2.user_id. Anyone can group by user_id and category; that only counts what one person bought and never relates two people. The trap is the join predicate: reach for <> and you emit every pair twice in mirror order plus self-pairs, doubling and corrupting the collaborative-filtering signal. Note what the count is: COUNT(*) over the self-join is the number of ways to pair one buyer's purchases in a category with the other buyer's, not a set intersection of the same product.


Break down the requirements

Step 1: Self-join the fact table to pair buyers

Join transactions t1 to transactions t2 on t1.user_id < t2.user_id. The strict inequality does double duty: it drops self-pairs (a user can never satisfy id < id) and it keeps only one of the two mirror orderings, so each unordered pair surfaces exactly once with the smaller user ID as user1.

Step 2: Scope the pairing to a single category

Bring in products twice, once per buyer, and require p.category = p2.category. Without that predicate the two sides fan out across every category combination and the pairing you count becomes meaningless.

Step 3: Count the pairings per category and pair

GROUP BY p.category, t1.user_id, t2.user_id and COUNT(*) counts, per category per pair, every way one buyer's purchase in that category lines up with the other buyer's. Each output row is one category and one canonical pair with user1 the smaller ID, holding the pairing count in shared_categories.


The solution

Per-category pairwise buyer overlap
SELECT p.category, t1.user_id AS user1, t2.user_id AS user2, COUNT(*) AS shared_categories
FROM transactions t1
INNER JOIN transactions t2 ON t1.user_id < t2.user_id
INNER JOIN products p ON t1.product_id = p.product_id
INNER JOIN products p2 ON t2.product_id = p2.product_id AND p.category = p2.category
GROUP BY p.category, t1.user_id, t2.user_id

Common follow-up questions