Big Spenders
An easy SQL interview practice problem on DataDriven. Write the query and run it against a real database, with instant feedback.
- Domain
- SQL
- Difficulty
- easy
- Seniority
- L3
Problem
Refunds and zero-dollar rows in transactions are not purchases, and our loyalty tier only opens to customers with at least 13 real purchases. For each customer who qualifies, report their total spend and purchase count, biggest spenders first.
The whale list.
Tables
transactions
Columns: transaction_id INTEGER, user_id INTEGER, product_id INTEGER, quantity INTEGER, total_amount REAL, transaction_date TEXT
| transaction_id | user_id | product_id | quantity | total_amount | transaction_date |
|---|---|---|---|---|---|
| 1067 | 197 | 1001 | 2 | 23.46 | 2026-02-02 |
| 1134 | 294 | 1050 | 3 | 36.93 | 2026-03-03 |
| 1201 | 391 | 1099 | 4 | 50.4 | 2026-04-04 |
| 1268 | 488 | 1148 | 5 | 63.87 | 2026-05-05 |
| 1335 | 585 | 1197 | 0 | 0 | 2026-06-06 |
| 1402 | 682 | 1246 | 2 | 90.81 | 2026-07-07 |
| 1469 | 779 | 1295 | 3 | 104.28 | 2026-08-08 |
| 1536 | 876 | 1344 | 4 | 117.75 | 2026-09-09 |
| 1603 | 973 | 1393 | 5 | -45 | 2026-01-10 |
| 1670 | 1070 | 1442 | 1 | 144.69 | 2026-02-11 |
Expected output
| user_id | lifetime_spend | tx_count |
|---|---|---|
| 1070 | 10511.76 | 14 |
| 973 | 10191.96 | 13 |
| 876 | 10134.6 | 14 |
| 779 | 9946.02 | 14 |
| 585 | 9491.52 | 13 |
Worked solution and explanation
This is a two-level filter dressed up as a loyalty program. One rule is about rows: a refund or a zero-dollar line is not a purchase. The other is about people: a customer earns the tier with at least 13 real purchases. Anyone can write the GROUP BY. What separates candidates is putting each rule on the level it lives on: drop the bad rows before grouping, judge customers after. Skip the row cleanup and a refund quietly counts as a purchase, pushing someone over the bar who never earned it while dragging their spend total down.
Two filters, two levels
Step 1: Throw out the non-purchases
WHERE total_amount > 0 removes the negative refund row and the zero-dollar row before anything is counted. It has to happen first: both the purchase count and the spend should only ever see real purchases.
Step 2: Roll up per customer
GROUP BY user_id, emitting SUM(total_amount) AS lifetime_spend and COUNT(*) AS tx_count. total_amount is already the line total, so it is summed as-is, never multiplied by quantity.
Step 3: Judge the customer, not the row
HAVING COUNT(*) >= 13. A purchase count does not exist until rows are grouped, so this test cannot live in WHERE. It runs on the cleaned count, which is exactly why step 1 had to come first.
Step 4: Biggest spenders first
ORDER BY lifetime_spend DESC. The sort key is a returned column, so the grader checks the order by spend and nothing else.
SELECT user_id,
SUM(total_amount) AS lifetime_spend,
COUNT(*) AS tx_count
FROM transactions
WHERE total_amount > 0
GROUP BY user_id
HAVING COUNT(*) >= 13
ORDER BY lifetime_spend DESC- Count first, ask later
- Grouping raw rows means
COUNT(*)includes the refund and the zero-dollar row. A customer with 12 real purchases plus a refund reads as 13 and gets in, and the refund shaves theirlifetime_spend. - Clean first, then count
WHERE total_amount > 0runs before the rollup, soCOUNT(*)andSUM(total_amount)both describe real purchases, andHAVINGjudges each customer on the honest number.
Where the interviewer goes next
Common follow-up questions
- Net refunds against spend instead of dropping them, but still count only positive rows as purchases. (Tests a conditional count such as `SUM(CASE WHEN total_amount > 0 THEN 1 ELSE 0 END)` beside a plain `SUM(total_amount)`.)
- Only count purchases from the last 90 days toward the bar. (Another row-level rule, so it joins the `WHERE` clause, not `HAVING`.)
- Add each customer's most recent purchase date. (Checks that `MAX(transaction_date)` slots in as one more aggregate per `user_id`.)
- Define the tier as the top 10% of customers by spend instead of a fixed purchase count. (Moves the bar from a literal to a relative cutoff computed over all customers.)