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

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

transactions

Columns: transaction_id INTEGER, product_id INTEGER, quantity INTEGER, total_amount REAL, transaction_date TEXT, user_id 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

Expected output

First 5 of 5 rows
product_namecategorytotal_revenue
Premium Widget 100XElectronics2713.98
Eco Kit 98XGarden2660.1
Smart Gadget 97XAutomotive2633.16
Classic System 96XBeauty2606.22
Essential Set 95XToys2579.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.

Top five products by booked revenue
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 NULL price, 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