Spending by Account Status

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
Asked in
Capital One

Problem

We're segmenting the platform's users by account status to see how revenue splits across them, and each status should count all of its users, even the ones who have never made a transaction. For each status with more than 50 transactions, show the transaction count, the number of unique users, and the total revenue, from highest revenue down.

Segment user spending and activity by account status across the platform

Tables

users

Columns: user_id INTEGER, username TEXT, email TEXT, signup_date TEXT, account_status TEXT, age_bucket TEXT

users: 10 of 200 rows
user_idusernameemailsignup_dateaccount_statusage_bucket
100alicealice@example.com2025-02-02inactive25-34
197aaron42aaron42@example.com2026-03-03suspended35-44
294 ameliaamelia@example.com2024-04-04pending_verification45-54
391arjun arjun@example.com2025-05-05active55-64
488ava99ava99@example.com2026-06-06inactive65+
585andrew_kandrew_k@example.com2024-07-07suspendedNULL
682anikaanika@example.com2025-08-08 16:34:00pending_verification18-24
779aidenaiden@example.com2026-09-09 14:53:00active25-34
876aria_baria_b@example.com2024-10-10inactive35-44
973brianbrian@example.com2025-11-11suspended45-54

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 3 of 3 rows
account_statustransaction_countuser_counttotal_revenue
pending_verification545037716.66
suspended545036735.72
inactive525036241.92

Worked solution and explanation

What this problem is really testing

Strip the business dressing and this is a grouped aggregate over a base you must not shrink, gated by a threshold that actually has to cut something. Anyone can group by account status and add up the money. Two moves separate the strong answers. First, the user tally has to count everyone in a status, buyers or not, so the join has to be a LEFT JOIN from users; reach for an inner join and every user who never transacted vanishes, quietly turning user_count into a buyer count. Second, the more-than-50-transactions cutoff is a filter on a per-group count, so it belongs in HAVING, not WHERE, and here it genuinely drops the 40-transaction 'active' status. Skip it and that status rides along in a report it does not qualify for.


Building the query

Step 1: Join from users, and keep the ones who never bought

Start from users and LEFT JOIN transactions on user_id. This keeps one row per user even when they have no matching transaction, so a status full of dormant accounts still contributes its people to the user count. An inner join would drop those users before you ever counted them.

Step 2: Aggregate each metric per status

GROUP BY u.account_status collapses to one row per status. COUNT(t.transaction_id) counts real transactions (NULLs from non-transacting users are ignored), COUNT(DISTINCT u.user_id) counts unique people so a heavy buyer with many transaction rows is still one user, and SUM(t.total_amount) totals the revenue.

Step 3: Filter on the aggregate with HAVING

HAVING COUNT(t.transaction_id) > 50 runs after the grouping, so it can test the per-group transaction count. WHERE cannot: it filters raw rows before the count exists. On this data that single line is what removes the 'active' status, which lands at 40 transactions.

Step 4: Order by revenue, highest first

ORDER BY total_revenue DESC puts the biggest earners first. pending_verification and 'suspended' both sit at 54 transactions, and that is harmless: the order is by revenue, not by count, and the three revenues differ.

The solution

Spending by account status
SELECT u.account_status,
       COUNT(t.transaction_id) AS transaction_count,
       COUNT(DISTINCT u.user_id) AS user_count,
       SUM(t.total_amount) AS total_revenue
FROM users u
LEFT JOIN transactions t ON u.user_id = t.user_id
GROUP BY u.account_status
HAVING COUNT(t.transaction_id) > 50
ORDER BY total_revenue DESC
Inner join
user_count = distinct buyers only; dormant accounts disappear from the tally.
LEFT join
user_count = every user in the status; the transaction count and revenue still come only from real transactions.

Common follow-up questions