New User Purchases
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
- L3
Problem
Growth wants to quantify how much revenue the 2026 signup cohort has generated. Sum all transaction spending for users who signed up during calendar year 2026 and return a single total.
What's this year's signup cohort worth so far?
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
| total_revenue |
|---|
| 46935.3 |
Worked solution and explanation
What this really is
This is a cohort filter whose condition lives in a different table from the money, dressed up as a revenue total. The amounts sit in transactions.total_amount; cohort membership is users.signup_date, and neither table can answer alone. Anyone can write the SUM. The trap is the date: transactions already carries transaction_date, it is the partition key, and filtering on it feels free. Do that and you report 2026 calendar revenue, not cohort revenue. Every older signup who bought something this year lands in the total, and the moment the calendar turns, the cohort's later spend falls out.
Getting there
Step 1: Locate the measure and the membership
The figure to add up is total_amount, one row per sale in transactions. Who belongs to the cohort is decided by signup_date, which only users holds. The join is not optional: it is the only way to carry cohort membership onto the sales.
Step 2: Join transactions to users on user_id
user_id is unique in users, so each sale matches exactly one buyer and no amount is counted twice. That is the cardinality to confirm before trusting a SUM over a join. Cohort members who never bought anything have no sales to add, so an inner join loses nothing.
Step 3: Filter on the signup year, not the sale year
strftime('%Y', u.signup_date) = '2026' reads the year off the stored text. Some signups carry a clock time ('2026-09-09 14:53:00' in the sample), so a careless signup_date <= '2026-12-31' would drop a late-December signup with a time part. There is no account_status filter: a suspended account's past spend is still the cohort's revenue.
Step 4: Collapse to one figure
SUM(t.total_amount) with no grouping returns the single total. Voided sales add zero and refunds subtract, so this is net revenue as recorded.
SELECT SUM(t.total_amount) AS total_revenue
FROM transactions t
JOIN users u ON t.user_id = u.user_id
WHERE strftime('%Y', u.signup_date) = '2026'Join for membership, filter on signup_date, sum total_amount.
- Sale-year filter
WHERE strftime('%Y', t.transaction_date) = '2026'sums every buyer's purchases dated 2026, no matter when they signed up. It runs fast and gives a plausible number for the wrong question.- Signup-year filter
WHERE strftime('%Y', u.signup_date) = '2026'fixes the population first, then counts all of its spend, whenever that spend landed. This is the cohort figure the question asks for.
Common follow-up questions
- How would you report the 2026 cohort's revenue per signup month? (Adds a grouping key derived from `signup_date` without touching the join.)
- Now count only each user's first 90 days after signup. What changes? (Tests a per-user window: `transaction_date` relative to that user's own `signup_date`, not a fixed calendar range.)
- Should refunds (a negative `total_amount`) count against the cohort? (Tests whether you raise a business decision instead of silently filtering rows.)
- What happens to a user whose `signup_date` is NULL? (A NULL year never equals '2026', so the user drops out silently; tests NULL awareness in predicates.)