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
| 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
| category | product_count | avg_rating | avg_price |
|---|---|---|---|
| Automotive | 10 | 4.35 | 909.025 |
| Beauty | 10 | 3.8200000000000003 | 793.091 |
| Books | 10 | 3.78 | 927.9119999999999 |
| Electronics | 9 | 3.755555555555555 | 922.1588888888889 |
| Garden | 9 | 3.822222222222222 | 823.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
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(*) > 8Common follow-up questions
- How would the categories change if you moved the rating test out of WHERE and expressed it as `HAVING AVG(rating) >= 3.0` instead? (Tests understanding of per-row (WHERE) versus per-group (HAVING) filtering and how the result set changes.)
- If two categories tied on average rating, how would you make the output order deterministic? (Tests awareness that ORDER BY must be total to be deterministic under ties.)
- The cutoff of more than eight is a fixed literal. How would you restructure the query to keep, say, the top quartile of categories by depth instead? (Tests replacing a static HAVING literal with a dynamic, data-driven threshold.)