Double or Nothing
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
Problem
A pricing analyst is flagging products on the same shelf that carry wildly different stickers. Within each category, find every pair where one product costs at least twice the other, listing each pair only once.
Same shelf, wildly different stickers. Spot the pricing gaps.
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
| product_1 | product_2 | category | price_1 | price_2 |
|---|---|---|---|---|
| Basic Device 1X | Basic Device 11X | Books | 17.52 | 92.82 |
| Basic Device 1X | Basic Device 21X | Books | 17.52 | 168.12 |
| Basic Device 1X | Basic Device 31X | Books | 17.52 | 243.42 |
| Basic Device 1X | Basic Device 41X | Books | 17.52 | 318.72 |
| Basic Device 1X | Basic Device 51X | Books | 17.52 | 394.02 |
Worked solution and explanation
What this really is
This is unordered pair enumeration wearing a pricing costume. Joining products to itself on category is the part everyone gets. What separates candidates is what comes after p1.product_id < p2.product_id: that guard orders each pair by id, and id says nothing about price, so the cheaper product can land on either side. Write only p1.price >= 2 * p2.price and you silently drop roughly half of the 978 qualifying pairs, every one where the lower id happens to be the cheaper product. Swap the < for != and you fail the other way, reporting every pair twice.
Break down the requirements
Step 1: Self-join products within a category
JOIN products p2 ON p1.category = p2.category AND p1.product_id < p2.product_id pairs each product with the higher-id products in its own category, and only those. The < does double duty: it blocks a product pairing with itself and it keeps exactly one ordering of each pair.
Step 2: Apply the 2x price condition
WHERE p1.price >= 2 * p2.price OR p2.price >= 2 * p1.price keeps a pair when either side is at least double the other. You need both directions because the ordering key is product_id, not price, so the cheaper product can sit on either side of the join.
Step 3: Return pair details
Select both product names, the shared category, and both prices. The category is shared, so p1.category is enough.
The solution
SELECT
p1.product_name AS product_1,
p2.product_name AS product_2,
p1.category,
p1.price AS price_1,
p2.price AS price_2
FROM products p1
JOIN products p2
ON p1.category = p2.category
AND p1.product_id < p2.product_id
WHERE p1.price >= 2 * p2.price
OR p2.price >= 2 * p1.priceCommon follow-up questions
- If `category` is NULL for some products, which pairs does your join quietly drop, and is that the behavior you want? (Tests null handling in the join key: NULL category never equals NULL, so those rows silently drop from an inner join.)
- How would you change the query so it only compares products that are both currently in stock? (Tests adding a filter predicate without breaking the dedup or ratio logic.)
- Suppose you also wanted the price ratio in the output, widest gap first. How would you compute and sort by it? (Tests computing a derived column and ordering by it, extending the pair logic.)