The Clean Aisle Numbers

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
L5
Asked in
PayPal

Problem

A customer's first purchase is the first row we recorded for them in transactions, the one with the lowest transaction_id, whatever its transaction_date says. Counting only those first purchases, we want the total amount each product category in products captured, largest first.

Clear the noise. What did each category actually earn?

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 10 rows
categorytotal_sales
Collectibles262.44
Home & Kitchen235.5
Toys212.04
Clothing208.56
Electronics144.69

Worked solution and explanation

What this problem really is

This is a first-row-per-customer problem wearing a category sales report. Anyone can join transactions to products and sum by category. What separates candidates is shrinking each customer to one row before anything is added up, and choosing that row by transaction_id, not by date. Every customer here has 12 to 14 transactions, so a plain sum by category reports lifetime spend and lands roughly ten times too high. Most customers also have later-recorded rows carrying earlier transaction_date values, so the 'earliest date' reading credits a different purchase and moves categories around the list.


Build it in three moves

Step 1: Number each customer's transactions by transaction_id

Number rows with ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY transaction_id). First means first recorded, and transaction_id is the column that records it. The dates on this table are not in recording order and they repeat, so ordering by transaction_date could pick the wrong row and also leave the winner undefined.

Step 2: Number before you join

Compute the numbering on transactions alone, then keep rn = 1. One customer's first purchase has a NULL product_id. Number first and that customer simply contributes nothing. Join to products first and the inner join drops that row, so their second purchase gets promoted to first place and credits a category it never earned.

Step 3: Join to products, sum, sort

Inner-join the numbered rows to products on product_id for the category, keep rn = 1, take SUM(total_amount) per category as total_sales, and sort by total_sales descending.

The solution

First recorded transaction per customer, totaled by category
WITH numbered_purchases AS (
    SELECT
        product_id,
        total_amount,
        ROW_NUMBER() OVER (
            PARTITION BY user_id
            ORDER BY transaction_id
        ) AS rn
    FROM transactions
)
SELECT p.category, SUM(f.total_amount) AS total_sales
FROM numbered_purchases f
JOIN products p ON f.product_id = p.product_id
WHERE f.rn = 1
GROUP BY p.category
ORDER BY total_sales DESC

Common follow-up questions