AVG() OVER() 不能直接计算“偏离均值”,必须先用 AVG() OVER(PARTITION BY ...) 计算分组均值,再用当前行值减去该均值;加 ORDER BY 会导致累计均值而非分组均值,引发逻辑错误。

AVG OVER 不能直接算“偏离均值”,得拆成两步
SQL 中 AVG() OVER() 只能计算滚动均值或分组均值,它本身不输出“该行值减去均值”的结果。所谓“偏离均值”,本质是:当前行的值 − 整体(或分组)均值。必须用窗口函数组合实现,不能只靠一个 AVG() OVER()。
正确写法:先算均值,再做减法
以按产品类别(category)计算各产品售价(price)偏离本类均值为例:
SELECT product_name, category, price, price - AVG(price) OVER (PARTITION BY category) AS deviation_from_category_avg FROM products;
关键点:
-
AVG(price) OVER (PARTITION BY category)先为每行算出其所属类别的平均价 - 用当前行的
price减去这个窗口均值,得到偏离量 - 若想算全局偏离(不按类),去掉
PARTITION BY即可:AVG(price) OVER () - 注意:
AVG()会自动忽略NULL,但减法中只要任一操作数为NULL,结果就是NULL
常见错误:误用 ORDER BY 导致逻辑错乱
如果在 AVG() OVER() 里加了 ORDER BY(比如 OVER (PARTITION BY category ORDER BY sale_date)),得到的是“截至当前行的累计均值”,不是整个分组的均值 —— 这会导致每个 deviation_from_category_avg 都不同,且无法反映真实偏离。
除非你明确要计算“时间序列上的滚动偏离”,否则 不要加 ORDER BY。验证方法:检查同一 category 下多行的 AVG(price) OVER (...) 值是否完全一致;不一致就说明被 ORDER BY 干扰了。
NULL 和数据类型容易踩坑
如果 price 是整型,而均值结果带小数,多数数据库(如 PostgreSQL、SQL Server)会返回 numeric/decimal 类型;但 SQLite 会截断为整数,导致偏离值失真。MySQL 8.0+ 默认保留小数,但若字段定义为 INT 且未显式转换,也可能隐式转整。
稳妥做法:
- 显式转浮点:
CAST(price AS DECIMAL(10,2)) - 或确保均值计算时至少有一个操作数是小数:
AVG(CAST(price AS REAL)) OVER (...) - 对
deviation_from_category_avg列做ROUND(..., 2)再输出,避免小数位过长干扰阅读
真正麻烦的不是语法,而是忘记“偏离”是二元运算——它依赖两个独立计算:一个标量均值(靠窗口函数拿到),一个逐行减法(靠普通表达式完成)。少一步,结果就不对。










