Normalization Tradeoffs in Practice

A hard SQL interview practice problem on DataDriven. Write the query and run it against a real database, with instant feedback.

Domain
SQL
Difficulty
hard
Seniority
L5
Asked in
Visa

Problem

The finance team needs total revenue per product category, where a transaction's revenue is the quantity sold times the product's listed price. Return each category with its revenue.

Clean data or fast queries? You can't always have both.

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

products

Columns: product_id INTEGER, product_name TEXT, category TEXT, price REAL, rating REAL, in_stock INTEGER

products: 10 of 200 rows
product_idproduct_namecategorypriceratingin_stock
1001Basic Device 1XBooks17.522.71
1050Deluxe Bundle 2XClothing25.054.41
1099Pro Unit 3XHome & Kitchen32.582.11
1148Ultra Tool 4XCollectibles40.113.81
1197Essential Set 5XToys47.641.51
1246Classic System 6XBeauty03.21
1295Smart Gadget 7XAutomotive62.74.90
1344Eco Kit 8XGarden70.232.61
1393Turbo Pack 9XMusic77.764.30
1442Premium Widget 10XElectronics85.2921

Expected output

First 5 of 11 rows
categorytotal_revenue
Automotive17505
Beauty10922.220000000001
Books13918.56
Clothing20855.25
Collectibles1555.1999999999998

Worked solution and explanation

The column that lies

This is a derived-measure aggregation dressed up as a finance report. The skill being probed: can you build revenue per row as t.quantity * p.price, a value that only exists after the join, and then sum it by category? Everyone writes the JOIN and the GROUP BY. What separates candidates is ignoring total_amount. It sits right there on transactions looking like the answer, but the spec defines revenue as quantity times the listed price. Sum total_amount instead and every category comes back wrong while still looking plausible.

Check the first sample row. Transaction 1067 bought 2 units of product 1001 at a listed 17.52, which is 35.04 of revenue. Its total_amount says 23.46. That gap repeats row after row, so no category total survives.

Trusting the stored total
SUM(t.total_amount) grouped by p.category. You still need the join to get category, so this saves nothing. It sums whatever the checkout recorded, discounts and old prices included, and misses the spec on every row.
Recomputing from the definition
SUM(t.quantity * p.price) grouped by p.category. The join already brings in price, so the multiplication costs nothing extra. Each row is priced by the definition the finance team gave.

Building it

Step 1: Join transactions to products on product_id

Both price and category live only on products, so no correct version skips the join. Use a plain inner JOIN. A transaction whose product_id has no catalog row has no price and no category, so it belongs in no total.

Step 2: Price each row before aggregating

Compute t.quantity * p.price inside the SUM, so each transaction's revenue is formed on its own row. The multiplication has to happen per row, after the join and before the sum.

Step 3: Group and order by p.category

GROUP BY p.category collapses the priced rows into one total per category, and ORDER BY p.category walks them alphabetically, as the prompt asks.

Revenue per category from quantity times listed price
SELECT p.category, SUM(t.quantity * p.price) AS total_revenue
FROM transactions t
JOIN products p ON t.product_id = p.product_id
GROUP BY p.category

Common follow-up questions