Transaction Overview

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
Meta

Problem

The finance team needs two numbers for the board slide: how many users have made at least one purchase, and the average transaction amount across all transactions.

The executive snapshot. Users, products, revenue.

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 1 of 1 rows
unique_purchasersavg_transaction_amount
15688.9572000000001

Worked solution and explanation

This is a grain question dressed up as a board-slide request. The two numbers come from the same table but at different grains: purchasers are counted per person, while the average is taken per transaction row. Anyone gets AVG(total_amount) right. The trap is the first number. Every row in transactions carries a user_id, so COUNT(user_id) looks like it counts users, but it counts purchase lines. On this data that gives 200 instead of 15. On the production table each buyer fans out to about twenty transactions, so the slide would claim twenty times the real customer base while the average beside it looks perfectly sane.

Read the grain of each number

Step 1: Count people, not purchase lines

user_id repeats on every transaction a person makes: each of the 15 buyers here sits on 12 to 14 rows. COUNT(DISTINCT user_id) collapses that fan-out, so a buyer with fourteen purchases counts once. A user who never bought has no row in transactions at all, so 'at least one purchase' needs no extra filter: appearing in the table is the condition.

Step 2: Average every transaction row

The second number is per transaction, so AVG(total_amount) runs over all 200 rows as recorded, including the -45.0 refund and the 0.0 voided sale. Averaging per-user subtotals first would weigh a one-time buyer the same as a heavy one, which answers a different question.

Step 3: Return both in one pass

Neither number is broken down by anything, so both aggregates sit in one SELECT with no GROUP BY. The result is one scan, one row, and two columns named for the slide.

Two aggregates in a single scan
SELECT
    COUNT(DISTINCT user_id) AS unique_purchasers,
    AVG(total_amount) AS avg_transaction_amount
FROM transactions
Counts purchase lines
COUNT(user_id) or COUNT(*) returns 200 here, one per transaction. Nothing errors, and the correct average next to it makes the row look trustworthy.
Counts buyers
COUNT(DISTINCT user_id) returns 15: each buyer once, no matter how many times they bought.

Common follow-up questions