Top Recent Sellers
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
- Vanguard
Problem
Our marketplace homepage is getting a three-slot trending widget, filled from the transactions table. It should show the products with the highest total sales amount over the last 30 days, largest first.
Fresh data, top sellers. The recent leaderboard.
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 |
Expected output
| product_id | total_sales |
|---|---|
| 5754 | 1330.05 |
| 5705 | 1316.58 |
| 5362 | 1222.29 |
Worked solution and explanation
This is a windowed revenue leaderboard in a homepage-widget costume. Everyone writes the GROUP BY product_id. What separates candidates is reading 'sales' as money and pinning 'last 30 days' to today. In this window nearly every product sold exactly once, so a COUNT(*) version ties them all at 1 and hands the widget an arbitrary three. Drop the cutoff and you sum every year of history: totals roughly double, and the top slot goes to product 5852, which has not sold in months.
Getting to the answer
Step 1: Pin the window to today
'Last 30 days' counts back from today: transaction_date >= DATE '2026-09-12' - INTERVAL '30 days'. CURRENT_DATE resolves to the same day here, so either spelling grades. transaction_date holds plain 'YYYY-MM-DD' strings, so it compares directly and there is no time of day to strip.
Step 2: Sum the money per product_id
SUM(total_amount) grouped by product_id. Refunds and voids sit in the table as negative and zero amounts, so they net into the total, which is what revenue should do.
Step 3: Sort, then cut to three
ORDER BY total_sales DESC and then LIMIT 3. The cut keeps whatever arrives first, so the sort has to happen before it. The third and fourth totals (1222.29 and 1208.82) do not tie, so the cut is clean.
SELECT product_id, SUM(total_amount) AS total_sales
FROM transactions
WHERE transaction_date >= DATE '2026-09-12' - INTERVAL '30 days'
GROUP BY product_id
ORDER BY total_sales DESC
LIMIT 3;- Counting sales
COUNT(*)perproduct_id: in this window each product sold once, so every product ties at 1 andLIMIT 3keeps whichever three the engine meets first.- Summing them
SUM(total_amount)perproduct_id: the totals separate cleanly (1330.05, 1316.58, 1222.29 at the top), so money decides the three slots.
Common follow-up questions
- Two products tie for the third slot. Which one gets it? (Tests adding a deterministic tiebreaker such as `product_id` to the sort before the `LIMIT`.)
- Marketing now wants the top three per `category`. (Tests moving from one global cut to a cut scoped per group, joining `products`.)
- Should a product whose only activity in the window is a refund ever show up? (Tests whether netted negatives can surface, and whether to filter on `total_sales > 0`.)