Recent Price Drops

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

Every product in products carries an in_stock flag, and every sale in transactions records the product_id sold and its transaction_date. List the ID and name of each product that is in stock or sold within the last seven days, one row per product.

The price just dropped. Who noticed?

Tables

products

Columns: product_id INTEGER, product_name TEXT, in_stock INTEGER, category TEXT, price REAL, 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, transaction_date TEXT, user_id INTEGER, quantity INTEGER, total_amount REAL

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 160 rows
product_idproduct_name
1001Basic Device 1X
1050Deluxe Bundle 2X
1099Pro Unit 3X
1148Ultra Tool 4X
1197Essential Set 5X

Worked solution and explanation

This is a set union dressed up as a freshness check. A product qualifies for one of two unrelated reasons: a flag on its own row in products, or a sale in transactions within the last seven days. Everyone spots both conditions. What separates candidates is noticing that only one of them needs transactions at all. Join first and filter on 'sold recently OR in stock' afterwards, and the inner join has already thrown away every product that never sold, which is over a hundred of the 200, in stock or not. The products that survive come back once per sale.

The trap: one join, one OR

The version that looks right
SELECT p.product_id, p.product_name
FROM products p
JOIN transactions t ON p.product_id = t.product_id
WHERE t.transaction_date >= DATE('now', '-7 day')
   OR p.in_stock = 1

Contrast: the OR runs after the inner join, so a product with no row in transactions never reaches it.

Build it as two lists

Step 1: Collect the products sold in the past week

Join products to transactions on product_id and keep sales with transaction_date >= DATE('now', '-7 day'). This side genuinely needs the join: the sale date lives only in transactions. Rows with a NULL product_id match nothing and drop out, which is right.

Step 2: Collect the in-stock products straight from products

in_stock = 1 is a fact about the product row, so read it from products alone. No join is the whole point: a product with zero sales is still a candidate here.

Step 3: Merge the two lists with UNION

A product can sit in both lists, in stock and sold this week. UNION (not UNION ALL) collapses that overlap so every product appears once. Both branches select product_id, product_name in the same order, which is what lets them stack.

The solution

Two branches, one merged list
SELECT DISTINCT p.product_id, p.product_name
FROM products p
JOIN transactions t ON p.product_id = t.product_id
WHERE t.transaction_date >= DATE('now', '-7 day')
UNION
SELECT product_id, product_name
FROM products
WHERE in_stock = 1

The join lives only in the branch that needs the sale date.

Join, then OR
Every row must survive the inner join to transactions, so products with no sales are gone before the OR runs, and repeat sales repeat the product.
Two branches, then UNION
The stock branch never touches transactions, the sales branch is narrowed to one row per product, and UNION removes products that qualify both ways.

Common follow-up questions