Profitable Categories by Price

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
L4
Asked in
Forbes

Problem

The products table holds one row per catalog item with its category, price, rating and an in_stock flag, and an out-of-stock item counts toward nothing. Report the lowest in-stock price and the average in-stock rating for every category whose in-stock items average a rating of at least 3.0.

The most profitable categories.

Tables

products

Columns: product_id INTEGER, product_name TEXT, category TEXT, price REAL, rating REAL, in_stock INTEGER

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

Expected output

First 3 of 3 rows
categorymin_priceavg_rating
Automotive1383.225
Collectibles40.113.8
Sports190.713.29375

Worked solution and explanation

What this really is

This is a two-grain filtering problem dressed up as a catalog report. There are two conditions, and they look alike because both sound like 'which products count'. They are not alike. The stock rule is about a single row: is this item on the shelf? The rating bar is about a whole category: do its in-stock items average 3.0 or better? Anyone can write the GROUP BY. What separates candidates is putting each condition at its own grain. Put the rating bar on the rows and every category clears it, every average is inflated, and the cheapest low-rated item vanishes from the minimum price.


Build it in order

Step 1: Decide which rows exist

WHERE in_stock = 1 runs before any grouping. Out-of-stock items are not part of this report at all, so they must not feed the average or the minimum. Skip it and both measures drift, which can move a category across the 3.0 line in either direction.

Step 2: Collapse to one row per category

GROUP BY category with MIN(price) and AVG(rating) computes both measures over the same eligible row set. Both aggregates skip NULLs, so the few unrated or unpriced items simply do not vote, which is what you want.

Step 3: Judge each category by its average

HAVING AVG(rating) >= 3.0 runs after the aggregation, on the category's number. This is the only place that condition can live: a WHERE never sees an average, only one row's rating. On this catalog the bar is real: some categories fall short of it and drop out.

In-stock categories that clear the rating bar
SELECT
    category,
    MIN(price) AS min_price,
    AVG(rating) AS avg_rating
FROM products
WHERE in_stock = 1
GROUP BY category
HAVING AVG(rating) >= 3.0
Rating bar on rows
WHERE in_stock = 1 AND rating >= 3.0 throws away every low-rated item before averaging. Each avg_rating is computed over items already at 3.0 or better, so no category can fail, and min_price ignores cheap items with weak ratings.
Rating bar on the group
WHERE in_stock = 1 then HAVING AVG(rating) >= 3.0 averages every in-stock item, good and bad, and only then asks whether the category as a whole clears the bar. Weak categories drop out; strong ones report honest numbers.

Common follow-up questions