加权平均成本 = Σ(单价 × 数量) / Σ(数量),SQL需用SUM(price*quantity)/SUM(quantity)实现,注意NULL处理与除零保护,分组时须GROUP BY且避免混用聚合与非聚合字段。

加权平均成本的数学定义和SQL实现难点
加权平均成本 = Σ(单价 × 数量) / Σ(数量),不能直接用 AVG(),因为 AVG(price) 会把每条记录等权重处理,而实际采购中不同批次的数量差异很大。
常见错误是写成 AVG(price * quantity) 或嵌套 AVG(AVG()),这两种都偏离业务含义:前者算的是“平均的金额”,后者语法非法且逻辑错乱。
标准写法:用 SUM() 手动实现分子分母
最可靠、跨数据库兼容的方式是显式写出加权公式:
SELECT SUM(price * quantity) / SUM(quantity) AS weighted_avg_cost FROM purchases;
这个写法在 PostgreSQL、MySQL 8.0+、SQL Server、Oracle 都能直接运行。注意两点:
-
price和quantity都必须为数值类型(NULL值会被SUM()自动忽略) - 如果全表
quantity总和为 0,部分数据库(如 PostgreSQL)会报division by zero错误,需加保护
防除零和空数据的健壮写法
生产环境必须考虑 SUM(quantity) = 0 或全 NULL 的情况。推荐用 NULLIF() 配合 COALESCE():
SELECT COALESCE( SUM(price * quantity) / NULLIF(SUM(quantity), 0), 0 ) AS weighted_avg_cost FROM purchases;
NULLIF(SUM(quantity), 0) 在分母为 0 时返回 NULL,使整个除法结果为 NULL;COALESCE(..., 0) 再将其转为 0(也可按需改为 NULL 或抛异常)。
别用 CASE WHEN SUM(quantity) = 0 THEN 0 ELSE ... END —— 多余且易漏掉 NULL 分支。
按用户分组计算时的常见陷阱
如果要算每个用户的加权平均成本,GROUP BY user_id 是必须的,但要注意:
- 所有非聚合字段(如
user_id)必须出现在GROUP BY中,否则 MySQL 5.7+ 严格模式或 PostgreSQL 会报错 - 不要在
SELECT里混用聚合与非聚合字段(比如SELECT user_id, AVG(price), SUM(price*quantity)/SUM(quantity)),除非明确AVG(price)是辅助指标 - 若某用户没有任何采购记录(
quantity全NULL),SUM(quantity)返回NULL,NULLIF(NULL, 0)仍是NULL,最终结果为NULL—— 这符合语义,不必强行补 0
加权平均本身不支持窗口函数直接简化,别试图用 AVG() OVER (PARTITION BY ...) 替代,它做不到加权。

















