Above the Curve

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

The growth team is building a high-value customer segment for an upcoming campaign. Compute each user's total spend, then surface only the users who land above the average customer's spend, showing their ID, total, and how far above that benchmark they sit. List the biggest spenders first.

Spenders who break from the pack.

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

Expected output

First 5 of 8 rows
user_idtotalabove_avg
107010511.761325.6640000000007
97310146.96960.8639999999996
87610134.6948.5040000000008
7799946.02759.9240000000009
6829757.44571.344000000001

Worked solution and explanation

Why this problem exists in real interviews

This is a grain-mismatch trap wearing a marketing costume. Anyone can group and sum; what separates candidates is where they measure the average. Compute AVG(total_amount) over transaction rows and you get the size of a typical purchase, a number far below what a typical customer spends over all their purchases, so nearly everyone clears the bar and the segment stops being high-value. The average has to be taken over the per-user totals, and HAVING alone cannot reach that overall number, which is exactly why the CTE plus scalar subquery is load-bearing here.


Break down the requirements

Step 1: Roll up to user grain

Aggregate transactions with GROUP BY user_id and SUM(total_amount). This is the unit the prompt cares about, not raw rows.

Step 2: Define the threshold

The cutoff is AVG(total) over the per-user totals, not AVG(total_amount) over transactions. Put the rollup in a CTE so the average runs against the right grain.

Step 3: Filter and project the delta

Keep users where total > (SELECT AVG(total) FROM totals), then project total - that_average as above_avg. The scalar subquery runs once.

Step 4: Sort biggest first

ORDER BY total DESC so the campaign team sees the whales at the top.


The solution

ABOVE AVERAGE USERS
WITH totals AS (
  SELECT user_id, SUM(total_amount) AS total
  FROM transactions
  GROUP BY user_id
)
SELECT
  user_id,
  total,
  total - (SELECT AVG(total) FROM totals) AS above_avg
FROM totals
WHERE total > (SELECT AVG(total) FROM totals)
ORDER BY total DESC

COMMON FOLLOW-UP QUESTIONS

Common follow-up questions