完整教程:【MYSQL】一文读懂 SQL 加权平均价计算:左连接、日期匹配与空值处理
一文读懂 SQL 加权平均价计算:左连接、日期匹配与空值处理
本文通过一个实际的 SQL 查询示例,带你逐步理解如何计算每个产品的加权平均售价,并处理没有销售记录的情况。
查询语句原文
SELECT
Prices.product_id,
IFNULL(
ROUND(
SUM(Prices.price * UnitsSold.units) / SUM(UnitsSold.units),
2
),
0
) AS average_price
FROM Prices
LEFT JOIN UnitsSold
ON Prices.product_id = UnitsSold.product_id
AND UnitsSold.purchase_date BETWEEN Prices.start_date AND Prices.end_date
GROUP BY Prices.product_id;
查询逻辑一览
示例数据表结构
Prices 表(价格表)
| product_id | price | start_date | end_date |
|---|---|---|---|
| 1 | 10 | 2025-01-01 | 2025-01-31 |
| 1 | 12 | 2025-02-01 | 2025-02-28 |
| 2 | 5 | 2025-01-01 | 2025-12-31 |
UnitsSold 表(销售表)
| product_id | purchase_date | units |
|---|---|---|
| 1 | 2025-01-15 | 2 |
| 1 | 2025-02-15 | 3 |
| 2 | 2025-03-01 | 0 |
| 3 | 2025-01-01 | 5 |
✅ 查询结果
| product_id | average_price |
|---|---|
| 1 | 11.20 |
| 2 | 0.00 |
关键点解析
1. 为什么用 LEFT JOIN?
- 为了保留
Prices表中所有产品,即使它在UnitsSold中没有销售记录。 - 如果用
INNER JOIN,没卖过的产品就不会出现在结果中。
2. ON 条件中的日期范围匹配
ON Prices.product_id = UnitsSold.product_id
AND UnitsSold.purchase_date BETWEEN Prices.start_date AND Prices.end_date
这个条件确保:
- 只将销售记录匹配到其购买日期所在的价格有效期内。
- 避免将销售记录与错误时间段的价格匹配。
冰棍小例子:
| 销售日期 | 生效价格 | 是否匹配 |
|---|---|---|
| 6月15日 | 1元 | ✅ |
| 6月15日 | 2元 | ❌(未生效) |
| 7月15日 | 1元 | ❌(已过期) |
| 7月15日 | 2元 | ✅ |
如果没有这个日期条件,你会把6月卖的冰棍也按7月的价格算,结果就错了!
3. 加权平均价格计算
SUM(Prices.price * UnitsSold.units) / SUM(UnitsSold.units)
- 分子:总销售额 = 每个价格 × 对应销售数量的总和
- 分母:总销售数量
- 结果就是加权平均价,销量多的价格权重更高。
4. 四舍五入与空值处理
ROUND(..., 2):保留两位小数IFNULL(..., 0):如果没有销售记录,结果为NULL,替换为0
❓ 常见疑问解答
Q1:ON 后面那句 AND ... BETWEEN ... AND ... 是什么意思?
AND UnitsSold.purchase_date BETWEEN Prices.start_date AND Prices.end_date
- 这是一个日期范围匹配条件,确保销售日期落在价格的有效期内。
- 等价于:
AND UnitsSold.purchase_date >= Prices.start_date AND UnitsSold.purchase_date <= Prices.end_date
Q2:SUM(Prices.price * UnitsSold.units) / SUM(UnitsSold.units) 怎么理解?
- 分子:总销售额(每笔销售的价格 × 数量之和)
- 分母:总销售数量
- 结果:加权平均售价,销量多的价格影响更大。
总结一句话
这段 SQL 的作用是:计算每个产品在其价格生效期间内的实际加权平均售价,即使没有销售记录也要显示,价格为 0。


浙公网安备 33010602011771号