CMD Guide
HomeDatabasesSQL Practice Problems

Average Selling Price

The average selling price of a product is not the average of its list prices — it is total money taken in divided by total units moved, so a price that applied to 100 sales must count 100 times while a price that applied to 15 sales counts 15 times. That single fact decides the whole query: you weight each price period by the units sold inside it, which is why the answer is SUM(units * price) / SUM(units) and never AVG(price).

The problem

Two tables. Prices gives each product a list of non-overlapping date windows, each with a price. UnitsSold records individual sales, each with a purchase_date and a unit count. For every product we want one row: its units-weighted average selling price, rounded to 2 decimals. Products that never sold must still appear (with 0).

Prices                                  UnitsSold
+------------+------------+----------+   +------------+---------------+-------+
| product_id | start_date | end_date | price |  product_id | purchase_date | units |
+------------+------------+----------+---+   +------------+---------------+-------+
| 1          | 2019-02-17 | 2019-02-28 |  5 |   | 1          | 2019-02-25    | 100   |
| 1          | 2019-03-01 | 2019-03-22 | 20 |   | 1          | 2019-03-01    | 15    |
| 2          | 2019-02-01 | 2019-02-20 | 15 |   | 2          | 2019-02-10    | 200   |
| 2          | 2019-02-21 | 2019-03-31 | 30 |   | 2          | 2019-03-22    | 30    |
+------------+------------+----------+---+   +------------+---------------+-------+

Notice a sale carries no price of its own. The price lives in Prices, keyed by which window the purchase_date falls into. So before we can do any arithmetic we must route each sale to its price.

Why a weighted average — the trap

It is tempting to write AVG(price) after the join. For product 1 that gives (5 + 20) / 2 = 12.50. But the real answer is 6.96. The two prices are nowhere near equally important: 100 units sold at 5, only 15 units at 20. AVG(price) treats both price rows as one vote each; the market does not. The correct mean is the volume-weighted one:

          SUM(units * price)     (100*5) + (15*20)     500 + 300     800
weighted = ------------------  =  -----------------  =  ---------  =  ---  = 6.9565… → 6.96
          SUM(units)              100 + 15              115           115

This is identical to SUM(revenue) / SUM(quantity) — the only definition of "average price" a finance or analytics team would accept. Memorize the shape: weight each value by the count it represents, sum the weighted values, divide by the sum of weights.

diagram
diagram

The query

SELECT p.product_id,
       IFNULL(ROUND(SUM(u.units * p.price) / SUM(u.units), 2), 0) AS average_price
FROM   Prices p
       LEFT JOIN UnitsSold u
              ON u.product_id    = p.product_id
             AND u.purchase_date BETWEEN p.start_date AND p.end_date
GROUP  BY p.product_id;

Three load-bearing pieces:

diagram
diagram

Traced end to end

After the range join, each sale sits next to the price that was in force on its date:

p.product_id | price | u.units | purchase_date | units*price
-------------+-------+---------+---------------+-----------
     1       |   5   |   100   | 2019-02-25    |   500     <- 02-25 in [02-17,02-28]
     1       |  20   |    15   | 2019-03-01    |   300     <- 03-01 in [03-01,03-22]
     2       |  15   |   200   | 2019-02-10    |  3000     <- 02-10 in [02-01,02-20]
     2       |  30   |    30   | 2019-03-22    |   900     <- 03-22 in [02-21,03-31]

Now GROUP BY product_id collapses each product and the SUMs run:

product_1: SUM(units*price)=500+300=800   SUM(units)=100+15=115   800/115 = 6.9565 -> 6.96
product_2: SUM(units*price)=3000+900=3900 SUM(units)=200+30=230   3900/230 = 16.9565 -> 16.96
+------------+---------------+
| product_id | average_price |
+------------+---------------+
|     1      |     6.96      |
|     2      |     16.96     |
+------------+---------------+

Why the naive version is wrong

Three rewrites that look reasonable and all break:

Pitfalls

Takeaways


Based on LeetCode 1251 "Average Selling Price" (the canonical Prices / UnitsSold dataset). Weighted-mean reasoning follows standard analytics practice (volume-weighted average price); NULL-aggregation and integer-division behavior cross-checked against the MySQL and PostgreSQL documentation. Re-authored and deepened for this guide to name the central insight — why a units-weighted average is required and how the LEFT-JOIN-to-NULL path makes IFNULL necessary — rather than narrate clauses.

🤖 Don't fully get this? Learn it with Claude

Stuck on Average Selling Price? Open Claude, copy a block below, and it'll teach you this exact concept — visually and interactively.

🎨 Explain it visually

Build the mental picture, not memorization.

I just read a lesson on **Average Selling Price** (Databases) and want to truly understand it. Explain Average Selling Price from first principles using ONE vivid real-world analogy and a visual mental model — draw it as ASCII art or a clear step-by-step diagram — with a concrete example using real numbers. Then ask me one question to check I got the mental picture, and wait for my reply. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🤔 Walk me through it (interactive)

Socratic — adapts to where you're stuck.

Teach me **Average Selling Price** interactively. Ask me ONE guiding question at a time, wait for my answer, and adapt to my confusion — build the idea with me step by step instead of explaining it all at once. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🧪 Quiz me & fix my gaps

Active recall exposes what you missed.

Quiz me on **Average Selling Price** with 5 questions, easy to tricky, ONE at a time. Tell me if each answer is right; at the end, explain clearly what I got wrong and why. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🧠 Make it stick

Intuition + hook + flashcards for long-term memory.

Help me remember **Average Selling Price** for the long term: give the one-sentence intuition, a memorable hook/mnemonic, a tiny worked example, and 3 active-recall flashcards (Q -> A). If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.

📝 My notes