Before the Rush

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

Problem

Ahead of holiday planning, a merchandising analyst is tracking how the revenue recorded in transactions moves from one calendar month to the next for each product category in products. Total the amounts per category and month, listed category by category with the months in time order.

The holidays are coming. See where each category's money moves, month by month.

Tables

transactions

Columns: transaction_id INTEGER, product_id INTEGER, total_amount REAL, transaction_date TEXT, user_id INTEGER, quantity INTEGER

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, category TEXT, product_name 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 131 rows
categorymonthtotal_revenue
Automotive2023-011824.96
Automotive2023-031555.5600000000002
Automotive2023-05238.98
Automotive2023-071016.76
Automotive2023-09777.78

Worked solution and explanation

What this really is

Underneath the holiday-planning story, this is a fact-and-dimension reconciliation with a date bucket on top. The revenue lives in transactions; the category lives in products; you have to marry them and roll the result up to one number per category per calendar month. Anyone can write the SUM. Two quieter decisions separate the strong answers: what a 'month' actually means, and what happens to a transaction whose product you can't identify.


Two ways to get plausible-but-wrong numbers

INNER JOIN (correct here)
A transaction whose product_id has no row in products has no known category, so it can't belong to any category bucket. Keeping only matched rows is the intended behavior.
LEFT JOIN
Keep the unmatched rows and they collect in a NULL-category bucket that isn't in the expected output, adding a phantom group the merchandising team never asked for.

Building the query

Step 1: Join on product_id

Match each transaction to its product on product_id to pull the category across. An inner join keeps only transactions whose product exists, which is exactly what you want here.

Step 2: Bucket the date to year-month

STRFTIME('%Y-%m', transaction_date) turns a date into a '2026-04' label. This is the grain decision: year plus month, never month alone.

Step 3: Sum within each category-month

GROUP BY category and the same year-month expression, then SUM(total_amount). The GROUP BY key has to match the two output dimensions exactly, one row per category-month pair.

Step 4: Order for reading

ORDER BY category, month lays each category out as a top-to-bottom time series, which is how the merchandising team reads a trend.


The solution

Join with year-month bucket and two-level GROUP BY
SELECT p.category,
    STRFTIME('%Y-%m', t.transaction_date) AS month,
    SUM(t.total_amount) AS total_revenue
FROM transactions t
JOIN products p ON t.product_id = p.product_id
GROUP BY p.category, STRFTIME('%Y-%m', t.transaction_date)
ORDER BY p.category, month

Common follow-up questions