Product Ratings vs Sales

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
L3

Problem

Do product categories that sell higher-rated items actually pull more revenue? An analyst wants each category's average rating across its sales in transactions, every sale counting at the rating of its product in products, next to the category's total revenue. Products that were never rated stay out of both figures.

Do higher ratings actually mean more revenue?

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

transactions

Columns: transaction_id INTEGER, user_id INTEGER, product_id INTEGER, quantity INTEGER, total_amount REAL, transaction_date TEXT

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 11 rows
categoryavg_ratingtotal_revenue
Automotive3.429411764705882611875.26
Beauty3.266666666666666611195.25
Books3.052941176470588510029.87
Clothing3.22352941176470611067.06
Collectibles4.3524.88

Worked solution and explanation

One filter, two aggregates

This is a scoping problem dressed up as a ratings dashboard. Revenue lives only in transactions, so the join is forced, and once you join, AVG(p.rating) runs over sale rows, which is exactly the per-sale average the question asks for. The real trap is the null filter. AVG already skips a NULL rating, so WHERE p.rating IS NOT NULL looks redundant and gets deleted. Do that and avg_rating comes out identical, the spot check passes, and every category holding an unrated product that sold ships an inflated total_revenue, because SUM(t.total_amount) never looks at the rating.

Each sale in `transactions` borrows its `category` and `rating` from `products`.
  • products: product_id PK, category, rating
  • transactions: transaction_id PK, product_id FK, total_amount
  • transactions.product_id references products.product_id

Build it in three moves

Step 1: Join transactions to products on product_id

Inner is the right join. A sale whose product_id is NULL has no category to land in, and a category with no sales has no revenue to report. After the join, each row is one sale wearing its product's rating and category.

Step 2: Drop unrated products in WHERE

Filter on p.rating IS NOT NULL before grouping. The filter is not there for the average, which skips NULLs on its own. It is there so the sales of unrated products never reach SUM(t.total_amount).

Step 3: Group by category and compute both measures

AVG(p.rating) over the joined rows weights each product by how often it sold, so a product sold three times votes three times. SUM(t.total_amount) adds the revenue as recorded, refunds and voided sales included.

Filter, join, then aggregate
SELECT
    p.category,
    AVG(p.rating) AS avg_rating,
    SUM(t.total_amount) AS total_revenue
FROM products p
JOIN transactions t ON p.product_id = t.product_id
WHERE p.rating IS NOT NULL
GROUP BY p.category
Filter dropped
avg_rating matches the correct answer to the last digit, because AVG skips NULL ratings. total_revenue grows in every category where an unrated product sold.
Filter kept
Both metrics describe the same population: sales of rated products. The average and the revenue now answer one question about one set of rows.

Common follow-up questions