The Final Sale
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
- L5
- Asked in
- Databricks
Problem
An analyst reviewing the catalog dashboard found sale figures that matched no single sale, when only each product's most recent transaction in transactions belongs on the page and products that never sold have nothing to show. List those products alphabetically by name from products, with each one's category and the amount and date of its latest sale.
Every product has a last sale. When was it?
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 |
transactions
Columns: transaction_id INTEGER, product_id INTEGER, user_id INTEGER, total_amount REAL, quantity INTEGER, transaction_date TEXT
| 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 | latest_sale_amount | last_sale_date |
|---|---|---|---|
| Basic Device 1X | Books | 225.51 | 2026-08-17 |
| Basic Device 21X | Books | 292.86 | 2026-04-22 |
| Basic Device 31X | Books | 427.56 | 2026-05-04 |
| Basic Device 41X | Books | 562.26 | 2026-06-14 |
| Basic Device 51X | Books | 696.96 | 2026-07-24 |
Worked solution and explanation
What this really is
This is greatest-one-per-group dressed up as a dashboard fix. The skill is keeping the amount and the date from the same physical row. The tempting GROUP BY product_id with MAX(transaction_date) and MAX(total_amount) computes each column on its own, so it can stamp a product's biggest-ever sale with its newest date: two transactions fused into one that never happened. Joining back on the max date is the other reflex, and it emits a product twice whenever two sales share that date.
Break down the requirements
Step 1: Rank transactions inside each product
Partition transactions by product_id and order newest-first on transaction_date. Break date ties on transaction_id descending, because IDs climb with each sale, so the pick stays deterministic when two sales land on the same day.
Step 2: Keep only the latest row
Filter to rn = 1. That leaves exactly one row per product, and its total_amount and transaction_date belong to the same sale.
Step 3: Attach product metadata with an inner join
Join the latest rows to products on product_id to pick up product_name and category. The inner join is what keeps never-sold products off the page. The null-product transactions also fall away here, because they match no product.
Step 4: Sort alphabetically by name
Order by product_name alone. Names are unique in products, so the alphabetical order is already settled and a second key would decide nothing.
The solution
WITH ranked AS (
SELECT product_id,
total_amount,
transaction_date,
ROW_NUMBER() OVER (
PARTITION BY product_id
ORDER BY transaction_date DESC, transaction_id DESC
) AS rn
FROM transactions
)
SELECT p.product_name,
p.category,
r.total_amount AS latest_sale_amount,
r.transaction_date AS last_sale_date
FROM products p
JOIN ranked r ON r.product_id = p.product_id
WHERE r.rn = 1
ORDER BY p.product_nameCommon follow-up questions
- What if the dashboard also wanted the two most recent sales per product? (Change the filter to `rn <= 2`. The rest of the query is identical.)
- Why break ties on `transaction_id` and not leave them to the engine? (Without it, two sales on the same `transaction_date` can swap between runs and the amount shown flickers.)
- How would you include products that have never sold, with NULL sale columns? (Use `products LEFT JOIN ranked` with the `rn = 1` condition moved into the join's ON clause.)
- How would the plan change at 1 billion transactions and 1 million products? (Partition `transactions` by date and maintain a per-product latest-sale table that is updated incrementally.)