加权平均是各值乘以对应权重后求和再除以权重总和,SQL中需用SUM(value*weight)/NULLIF(SUM(weight),0)实现,而非AVG();例如按销量加权计算地区平均单价时,分母必须为SUM(quantity)并防除零。

什么是加权平均,为什么不能直接用 AVG()
加权平均不是所有值简单相加再除以个数,而是每个值乘以对应权重后求和,再除以权重总和。比如学生成绩里“期末占60%、平时占40%”,直接用 AVG() 会把平时分和期末分当成同等重要,结果完全失真。
SQL 没有内置的 WEIGHTED_AVERAGE() 函数,必须手动构造:用 SUM(value * weight) / SUM(weight)。这是唯一通用且语义清晰的做法。
标准写法:在 GROUP BY 中计算每组加权平均
假设有一张销售表 sales,字段为 region(地区)、price(单价)、quantity(销量),想算每个地区的加权平均单价(按销量加权):
SELECT region, SUM(price * quantity) / SUM(quantity) AS weighted_avg_price FROM sales GROUP BY region;
关键点:
-
price * quantity是“金额”——即带权重的贡献值 - 分母必须是
SUM(quantity),不是COUNT(*)或SUM(1),否则权重被忽略 - 如果某组
quantity全为NULL或 0,除零会报错或返回NULL,需加NULLIF()防御
处理 NULL 和零权重:避免除零和意外 NULL
当权重列含 NULL 或全为 0 时,SUM(weight) 返回 NULL 或 0,导致整个表达式为 NULL 或报错(如 PostgreSQL 会抛 division by zero)。稳妥写法:
SELECT region, SUM(price * quantity) / NULLIF(SUM(quantity), 0) AS weighted_avg_price FROM sales GROUP BY region;
NULLIF(SUM(quantity), 0) 在分母为 0 时返回 NULL,使整行结果为 NULL 而非中断;若需默认值(如 0),可套 COALESCE(..., 0)。
注意:quantity 本身为 NULL 的行会被 SUM() 自动跳过,无需额外 WHERE quantity IS NOT NULL ——但如果你希望把 NULL 当 0 处理,就得显式用 COALESCE(quantity, 0)。
不同数据库的兼容性细节
绝大多数 SQL 引擎(PostgreSQL、SQL Server、Oracle、SQLite)都支持上述写法。但有两个常见差异:
- MySQL 8.0+ 支持窗口函数,但加权平均仍需手写;老版本不支持
NULLIF?可用CASE WHEN SUM(quantity) = 0 THEN NULL ELSE ... END替代 - BigQuery 要求显式类型转换:若
price是FLOAT64、quantity是INT64,乘积可能被截断,建议写成CAST(price AS FLOAT64) * quantity - 某些引擎(如 Presto)对空组返回
NULL更严格,务必确认业务是否允许该行为
加权平均的“权”必须是数值型、非负(负权重数学上可行但极少见,且多数业务场景中无意义),实际使用前先 CHECK 或 WHERE weight >= 0 更稳妥。

















