Product Revenue Ranking
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
- L3
- Asked in
- FinThrive
Problem
Our homepage feature is supposed to show the five products that bring in the most revenue, but the current pick was made by hand and we don't trust it. From the sales recorded in transactions, show each of those products with its name, category and total revenue, highest first.
Rank them by revenue. See who leads.
Tables
products
Columns: product_id INTEGER, product_name TEXT, category TEXT, price REAL, in_stock INTEGER, rating REAL
| 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 |
transactions
Columns: transaction_id INTEGER, product_id INTEGER, quantity INTEGER, total_amount REAL, transaction_date TEXT, user_id 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_name | category | total_revenue |
|---|---|---|
| Premium Widget 100X | Electronics | 2713.98 |
| Eco Kit 98X | Garden | 2660.1 |
| Smart Gadget 97X | Automotive | 2633.16 |
| Classic System 96X | Beauty | 2606.22 |
| Essential Set 95X | Toys | 2579.28 |
Worked solution and explanation
This is a top-five over a one-to-many rollup dressed up as a homepage pick. Anyone can write the join. What separates candidates is where the revenue number comes from. transactions already books what each sale earned in total_amount, refunds and voids included. Rebuild it as quantity * price from the catalog and you feature a different five products.
Where the revenue lives
Step 1: Total the booked amount per product
Every top product sells twice on this seed, so the answer is a per-product sum, not a sort over sales rows. Sort raw rows and 'Premium Widget 100X' shows up twice at 1356.99 instead of once at 2713.98. Sum total_amount, and keep p.product_id in the GROUP BY so two products that ever share a name stay separate.
Step 2: Bring the display columns from products
product_name and category exist only in products; the amounts exist only in transactions. An inner join on product_id is the right shape: a product with no sales has no revenue to feature, and the 18 sales with a NULL product_id match nothing and drop out.
Step 3: Sort by the total and cut at five
ORDER BY total_revenue DESC LIMIT 5. The sixth product sits at 2552.34, well clear of the fifth at 2579.28, so no tie straddles the cut on this data and no tiebreaker is needed.
SELECT p.product_name, p.category, SUM(t.total_amount) AS total_revenue
FROM products p
JOIN transactions t ON p.product_id = t.product_id
GROUP BY p.product_id, p.product_name, p.category
ORDER BY total_revenue DESC
LIMIT 5- Recomputed from the catalog
SUM(t.quantity * p.price)trusts the list price. Five products that sold have a NULLprice, so their sales vanish from the sum. 'Classic System 6X' lists at 0.0 while its sale booked 90.81. The resulting top five is a different list.- Booked on the sale
SUM(t.total_amount)adds what each sale recorded. The -45.00 refund and the 0.00 voided sale land against their products, and the five totals match what the ledger says those products earned.
Common follow-up questions
- How would you feature the top five per `category` instead of overall? (Moves the cut inside each group.)
- Two products tie for fifth: how do you decide which one is featured, and where does the query say it? (Tests a deterministic tie rule placed before `LIMIT`.)
- Should the -45.00 refund count against its product, or should the feature use gross sales only? (Tests gross versus net revenue definitions on `total_amount`.)