Popular Categories

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
L3

Problem

The merchandising team is choosing which categories to pitch for premium shelf space and only cares about ones with real depth of well-reviewed inventory, counting only products rated 3.0 or higher. Find the categories that carry more than eight such products, and for each show how many there are along with their average rating and average price.

Merchandising only cares about categories big enough to negotiate shelf space.

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 5 of 6 rows
categoryproduct_countavg_ratingavg_price
Automotive104.35909.025
Beauty103.8200000000000003793.091
Books103.78927.9119999999999
Electronics93.755555555555555922.1588888888889
Garden93.822222222222222823.1522222222222

Worked solution and explanation

The two filters hiding in one question

This is a two-stage filter wearing a merchandising costume: a WHERE that narrows the raw rows down to well-reviewed products, then a HAVING that keeps only the categories deep enough in them. Both are load-bearing, and here is why. Every category in this catalog carries the same twenty products, so a plain count ties them all and separates nothing. The discrimination only shows up once you count the products that clear the rating bar, and that per-category count does not exist until the rows are grouped, which is exactly what WHERE cannot reach. The trap is folding the two tests into one stage: put the count in the WHERE and it is illegal, put the rating in the HAVING and you scope categories on the wrong thing. Either way you hand back every category or the wrong ones.


Break it down

Step 1: Filter the raw products first

WHERE rating >= 3.0 runs before any grouping, so only well-reviewed products survive to be counted. Rows with a NULL rating drop out here, which is what you want: no rating is not a rating of three.

Step 2: Collapse to one row per category

GROUP BY category collapses the surviving rows into one group per category.

Step 3: Compute the three metrics

COUNT(*), AVG(rating) and AVG(price) describe the well-reviewed slice of each category.

Step 4: Keep only the deep categories

HAVING COUNT(*) > 8 runs after grouping and drops any category holding eight or fewer qualifying products. This test cannot live in WHERE: the per-category count does not exist until the rows are grouped.

Step 5: Order by the computed rating

ORDER BY avg_rating DESC leads with the best-reviewed categories, the way the team opens its pitch. The seven qualifying categories all land on distinct averages, so the order is deterministic without a tiebreaker.


The solution

Two-stage filter for shelf-worthy categories
SELECT
    category,
    COUNT(*) AS product_count,
    AVG(rating) AS avg_rating,
    AVG(price) AS avg_price
FROM products
WHERE rating >= 3.0
GROUP BY category
HAVING COUNT(*) > 8

Common follow-up questions