The Weight of Things

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
mid
Asked in
Visa

Problem

Some of this year's transactions point at a product_id that has no row in products, so that spend has no category and counts toward neither a category's revenue nor the total it is measured against. An analyst tracking where 2026 spend concentrates wants each category's revenue and its share of that categorized total as an unrounded fraction between 0 and 1, biggest categories first.

Every category claims part of the year. Find where it gathers.

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
categorycategory_revenuerevenue_share
Electronics15016.80.19862483175457754
Toys12090.780.15992282931660615
Books6363.450.08416834383015466
Clothing6363.450.08416834383015466
Home & Kitchen61110.08082922772176651

Worked solution and explanation

What this problem is really testing

Underneath the merchandising story this is a part-to-whole ratio that lives at two grains in one result: the numerator is per-category revenue, the denominator is the grand total of exactly those categories. Anyone can write the join and the GROUP BY. What separates candidates is where the denominator comes from. The reflex is a scalar subquery over transactions, and that total runs over a different population: it drops the year filter, or it keeps spend whose product_id never matched a category. Either way the shares stop summing to 1, and merchandising misreads how concentrated this year's spend really is, silently, because the query still returns plausible numbers.


Break down the requirements

Step 1: Join spend to its category

transactions carries the money in total_amount but not the category; products carries category. Inner-join on product_id so every dollar lands under a category. An inner join is correct here: a transaction with no matching product has no category to attribute to.

Step 2: Scope to the year

strftime('%Y', t.transaction_date) = '2026' keeps only this year's spend. Put it in WHERE so it runs before the aggregation, and so the numerator and the denominator both see only this year's rows.

Step 3: Sum per category, then divide by the whole

GROUP BY category gives SUM(total_amount) per category. The trick is the denominator: SUM(SUM(t.total_amount)) OVER () sums the per-category totals across the entire result set in the same pass. The inner SUM aggregates within each group; the outer windowed SUM adds those group totals together. Multiply the numerator by 1.0 to force real division instead of integer truncation, and leave the ratio unrounded so it stays an exact fraction of the whole.


The solution

Per-category revenue and its share of the year
SELECT p.category AS category,
       SUM(t.total_amount) AS category_revenue,
       SUM(t.total_amount) * 1.0 / SUM(SUM(t.total_amount)) OVER () AS revenue_share
FROM transactions t
JOIN products p ON t.product_id = p.product_id
WHERE strftime('%Y', t.transaction_date) = '2026'
GROUP BY p.category
ORDER BY category_revenue DESC

Common follow-up questions