Priciest Item in Each Category
A medium SQL interview practice problem on DataDriven. Write the query and run it against a real database, with instant feedback.
- Domain
- SQL
- Difficulty
- medium
- Seniority
- L4
- Asked in
- Amazon
Problem
When two products share a category's top price, neither is more expensive than the other, so the merchandiser building the 'priciest in the aisle' strip wants every product at that price shown. From the products table, list each category's priciest products with their price, categories alphabetically and the products within a category alphabetically by name.
The most expensive item per category.
Tables
products
Columns: product_id INTEGER, product_name TEXT, category 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 | product_name | price |
|---|---|---|
| Automotive | Eco Widget v97 | 1831.91 |
| Beauty | Smart Set v96 | 1822.64 |
| Books | Deluxe Widget v91 | 1776.29 |
| Clothing | Pro Set v92 | 1785.56 |
| Collectibles | Ultra Tool 14X | 115.41 |
Worked solution and explanation
What this really is
This is a per-group top-1 dressed up as a merchandising strip, and what separates candidates is what happens at the top of each aisle. Anyone can find the highest price. The skill is returning the product that carries it, once per category, without quietly choosing one product when two share that price. Compute the top over the whole catalog and you get one row, Electronics at 1859.72, instead of eleven. Number the rows per category and keep the first, and your answer matches today's catalog by luck, then drops a tied product the first day two items share a category's top price.
Step 1: Scope the top price to each category
The question is per aisle, so the comparison has to restart in every category. PARTITION BY category does that: Collectibles, with its two products, is compared only against itself. That is why its winner is 'Ultra Tool 14X' at 115.41 while every other aisle tops out above 1,700.
Step 2: Give tied prices the same position
DENSE_RANK() OVER (PARTITION BY category ORDER BY price DESC) gives position 1 to every product at a category's top price. That is the whole tie rule in one function: the ranking keeps the promise in the statement, not a later cleanup.
Step 3: Keep position 1, then sort for the shopper
Filter rnk = 1 in the outer query, because a window result cannot be filtered in the same SELECT that computes it. Then sort by category and product_name, the order the page shows. Both keys are output columns, so a tied pair lands alphabetically with no extra tiebreaker.
SELECT category, product_name, price
FROM (
SELECT
category,
product_name,
price,
DENSE_RANK() OVER (PARTITION BY category ORDER BY price DESC) AS rnk
FROM products
) ranked
WHERE rnk = 1
ORDER BY category, product_nameDENSE_RANK keeps all tied winners; the outer query filters to position 1 and sorts for browsing.
- One row per category
ROW_NUMBER()gives tied products positions 1 and 2 in whatever order the engine reads them.rn = 1keeps one of them, and which one can change between runs.- Every product at the top price
DENSE_RANK()gives both position 1, sornk = 1keeps both. On this catalog the two queries return the same 11 rows; only this one stays correct when a price ties.
Common follow-up questions
- On Postgres, `ORDER BY price DESC` puts `NULL` prices first, and nine products here have no price. What would your query return there, and how do you fix it? (Tests NULL ordering across engines and `NULLS LAST`.)
- The page now wants the top three price points per category, ties included. What changes? (Tests the difference between `RANK` and `DENSE_RANK` once the filter goes past position 1.)
- How would you write this without a window function? (Tests a grouped maximum joined back to `products` on `category` and `price`, which also keeps ties.)