Whale Watch

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

The transactions table holds one row per purchase, and total_amount is already that purchase's dollar value. For every user with more than a dozen transactions, report their total spending, how many transactions they made, and their average transaction size rounded to two decimals, biggest spender first.

The accounts driving the top line.

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 10 rows
user_idtotal_spendtxn_countavg_txn_size
107010511.7614750.84
97310146.9614724.78
87610134.614723.9
7799946.0214710.43
6829757.4414696.96

Worked solution and explanation

What this is really asking

Strip away the dashboard language and this is a filter on something no single row knows. Each row in transactions is one purchase; none of them carries 'this user has bought thirteen times'. That fact only exists once the rows are rolled up per user_id. Anyone can write the three aggregates. What separates candidates is where the threshold lives: put it on the roll-up, keep it strictly above twelve, and the list is exactly the regulars. Get either wrong and users with twelve purchases slip in, or the query refuses to run.

Step 1: Roll each user up once

Group by user_id and compute SUM(total_amount), COUNT(*) and AVG(total_amount) in one pass. total_amount is already the dollar value of the purchase, so multiplying by quantity double counts.

Step 2: Filter on the per-user count

The threshold is a property of the group, so it belongs in HAVING COUNT(*) > 12. 'More than a dozen' is strict: a user with exactly twelve purchases is not on the list, and on this data some users sit right at twelve.

Step 3: Round late, sort by spend

ROUND(AVG(total_amount), 2) happens in the output, never before averaging. Then ORDER BY total_spend DESC puts the biggest spender at the top.

Regulars, measured
SELECT
    user_id,
    SUM(total_amount) AS total_spend,
    COUNT(*) AS txn_count,
    ROUND(AVG(total_amount), 2) AS avg_txn_size
FROM transactions
GROUP BY user_id
HAVING COUNT(*) > 12
ORDER BY total_spend DESC
Row filter
WHERE COUNT(*) > 12 is rejected outright: WHERE runs before any grouping, when each row is still a single purchase and there is nothing to count.
Group filter
HAVING COUNT(*) > 12 runs after the roll-up, when every user is one row holding their count. It is the only place the threshold can see what it is testing.

Common follow-up questions