Total User Spend
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
- Asked in
- Etsy
Problem
Show each user's ID, username, and total transaction amount, alphabetically by username.
Each customer's total. Summarized.
Tables
users
Columns: user_id INTEGER, username TEXT, email TEXT, signup_date TEXT, account_status TEXT, age_bucket TEXT
| user_id | username | signup_date | account_status | age_bucket | |
|---|---|---|---|---|---|
| 100 | alice | alice@example.com | 2025-02-02 | inactive | 25-34 |
| 197 | aaron42 | aaron42@example.com | 2026-03-03 | suspended | 35-44 |
| 294 | amelia | amelia@example.com | 2024-04-04 | pending_verification | 45-54 |
| 391 | arjun | arjun@example.com | 2025-05-05 | active | 55-64 |
| 488 | ava99 | ava99@example.com | 2026-06-06 | inactive | 65+ |
| 585 | andrew_k | andrew_k@example.com | 2024-07-07 | suspended | NULL |
| 682 | anika | anika@example.com | 2025-08-08 16:34:00 | pending_verification | 18-24 |
| 779 | aiden | aiden@example.com | 2026-09-09 14:53:00 | active | 25-34 |
| 876 | aria_b | aria_b@example.com | 2024-10-10 | inactive | 35-44 |
| 973 | brian | brian@example.com | 2025-11-11 | suspended | 45-54 |
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 | username | total_spend |
|---|---|---|
| 294 | amelia | 9003.12 |
| 197 | aaron42 | 8814.54 |
| 779 | aiden | 9946.02 |
| 100 | alice | 8605.98 |
| 585 | andrew_k | 9491.52 |
Worked solution and explanation
What this really is
This is a one-to-many collapse dressed up as a customer roster. users holds the name and transactions holds the money, and every buyer has a dozen or more purchases, so the join hands you one row per purchase, not one per person. Anyone can write the join. What separates candidates is remembering that the join multiplies rows and that SUM has to fold them back to one row per user_id. Skip that and the username repeats down the page with a single purchase amount beside it: it looks like a report and is wrong on every line.
Build it
Step 1: Join users to transactions on user_id
Neither table answers the question alone: transactions has total_amount but no name, and users has username but no money. Join them on user_id. An inner join is the right call, because someone with no purchases has no spend to report, and the expected output has 15 rows, exactly the buyers.
Step 2: Fold each user's purchases into one total
Group by u.user_id and u.username and take SUM(t.total_amount). user_id is the real key; username rides along because the select needs it for display. The sum keeps the voided sale at 0.0 and the refund at -45.0, which is what spend net of refunds means.
Step 3: Sort by name, not by money
ORDER BY u.username ascending. Sorting on a non-aggregated column is legal here because username is part of the grouping.
The solution
SELECT u.user_id, u.username, SUM(t.total_amount) AS total_spend
FROM users u
JOIN transactions t ON u.user_id = t.user_id
GROUP BY u.user_id, u.username
ORDER BY u.usernameCommon follow-up questions
- What if you needed to include users with zero transactions, showing 0? (Switch to `LEFT JOIN` and wrap the total in `COALESCE(SUM(t.total_amount), 0)`.)
- How would you show only users whose spend is above a threshold? (Tests `HAVING` versus `WHERE` for filters on an aggregate.)
- What if usernames were not unique? (Grouping by `user_id` keeps the totals correct; `username` is only for display.)