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
| transaction_id | user_id | product_id | quantity | total_amount | transaction_date |
|---|---|---|---|---|---|
| 1067 | 197 | 1001 | 2 | 23.46 | 2026-02-02 |
| 1134 | 294 | 1050 | 3 | 36.93 | 2026-03-03 |
| 1201 | 391 | 1099 | 4 | 50.4 | 2026-04-04 |
| 1268 | 488 | 1148 | 5 | 63.87 | 2026-05-05 |
| 1335 | 585 | 1197 | 0 | 0 | 2026-06-06 |
| 1402 | 682 | 1246 | 2 | 90.81 | 2026-07-07 |
| 1469 | 779 | 1295 | 3 | 104.28 | 2026-08-08 |
| 1536 | 876 | 1344 | 4 | 117.75 | 2026-09-09 |
| 1603 | 973 | 1393 | 5 | -45 | 2026-01-10 |
| 1670 | 1070 | 1442 | 1 | 144.69 | 2026-02-11 |
products
Columns: product_id INTEGER, category TEXT, product_name TEXT, price REAL, rating REAL, in_stock INTEGER
| product_id | product_name | category | price | rating | in_stock |
|---|---|---|---|---|---|
| 1001 | Basic Device 1X | Books | 17.52 | 2.7 | 1 |
| 1050 | Deluxe Bundle 2X | Clothing | 25.05 | 4.4 | 1 |
| 1099 | Pro Unit 3X | Home & Kitchen | 32.58 | 2.1 | 1 |
| 1148 | Ultra Tool 4X | Collectibles | 40.11 | 3.8 | 1 |
| 1197 | Essential Set 5X | Toys | 47.64 | 1.5 | 1 |
| 1246 | Classic System 6X | Beauty | 0 | 3.2 | 1 |
| 1295 | Smart Gadget 7X | Automotive | 62.7 | 4.9 | 0 |
| 1344 | Eco Kit 8X | Garden | 70.23 | 2.6 | 1 |
| 1393 | Turbo Pack 9X | Music | 77.76 | 4.3 | 0 |
| 1442 | Premium Widget 10X | Electronics | 85.29 | 2 | 1 |
Expected output
| category | month | total_revenue |
|---|---|---|
| Automotive | 2023-01 | 1824.96 |
| Automotive | 2023-03 | 1555.5600000000002 |
| Automotive | 2023-05 | 238.98 |
| Automotive | 2023-07 | 1016.76 |
| Automotive | 2023-09 | 777.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_idhas 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
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, monthCommon follow-up questions
- How would you add a month-over-month growth rate per category on top of these totals? (Tests window functions layered over an aggregated series.)
- If a product could change category over time, how would you attribute each transaction's revenue to the right category? (Tests point-in-time / slowly-changing-dimension attribution.)
- How would you restrict this to a single year without scanning all 80M rows? (Tests partition pruning on `transaction_date`.)