加权平均必须用sum(valueweight)/sum(weight),因avg()仅算术平均;误用avg(value)、avg(valueweight)或count(*)作分母均错误,且需防null、除零及类型截断。

直接用 AVG() 得到的是算术平均,不是加权平均;必须手写 SUM(value * weight) / SUM(weight),否则结果一定错。
为什么不能用 AVG() 或 AVG(value * weight)
加权平均的数学定义是「总加权值 ÷ 总权重」,而 AVG() 只对输入值做无差别平均。常见错误包括:
-
AVG(value)完全忽略weight列,所有行等权处理 -
AVG(value * weight)先乘再平均,相当于把加权值当新序列求算术平均——比如两行(value=10, weight=1)和(value=20, weight=3),AVG(value * weight)得(10 + 60) / 2 = 35,但正确加权平均是(10×1 + 20×3) / (1+3) = 17.5 - 误用
COUNT(*)替代SUM(weight)作分母,导致权重归一化失效
分组加权平均的写法与关键细节
按 region 算加权单价,核心是 GROUP BY region 后分别聚合分子和分母:
- 分子必须是
SUM(price * quantity),不是AVG(price) * SUM(quantity)或其他变形 - 分母必须是
SUM(quantity),不能放进GROUP BY,否则每行单独计算,失去聚合意义 - 权重列(如
quantity)若含NULL,整行被SUM()自动跳过;但若整组quantity全为NULL,分母为NULL,整个表达式返回NULL - 业务上要求排除零权重?加
WHERE quantity > 0;允许零权重但防除零?用NULLIF(SUM(quantity), 0)
防除零、类型截断与 NULL 处理的实际写法
真实查询里,这三个问题最容易导致结果异常或报错:
- PostgreSQL 遇到分母为 0 直接抛
division by zero;MySQL 默认返回NULL(严格模式下也报错),所以必须用NULLIF(SUM(weight), 0) - 整数除法可能截断:比如
SUM(score * credit)和SUM(credit)都是整型,199 / 100在 SQL Server 或旧版 MySQL 中得1;加* 1.0强制转浮点,如SUM(score * credit) * 1.0 / NULLIF(SUM(credit), 0) -
value为NULL时,value * weight是NULL,整行被SUM()忽略——这通常符合预期,但需确认是否真该剔除该记录(比如缺考学生成绩为NULL,是否应计入分母?)
窗口函数中实现组内加权平均
想给每条记录附上它所在组的加权平均值(比如每个订单显示“本品类加权均价”),不能套 AVG() OVER ():
- 仍要手写:
SUM(value * weight) OVER (PARTITION BY group_col) / NULLIF(SUM(weight) OVER (PARTITION BY group_col), 0) - 两个
OVER子句的PARTITION BY必须完全一致,否则分子分母错位 - SQLite 不支持窗口函数,MySQL 8.0+、PostgreSQL、SQL Server 支持,但语法无差异
- 动态权重(如按时间衰减)需在
SUM()内联用CASE构造,且分子分母的CASE逻辑必须严格一致
最易被忽略的是权重列的语义一致性:同一列在分子和分母中必须代表相同含义,且不能因 NULL 或隐式类型转换悄悄改变参与计算的行集合——差一行,结果就偏了。










