avg() over() 必须带空括号,否则报错;加 order by 会变为累积平均,非全局均值;where 先于窗口函数执行,不可在 where 中引用 over() 结果;浮点误差需 round 或 cast 控制精度。

AVG() OVER() 必须带空括号,否则直接报错
写 AVG(sales) OVER(没括号)会触发类似 ERROR: window function requires an OVER clause 的错误。数据库要求显式声明窗口——哪怕什么都不限定,也得写 OVER()。这是语法硬性门槛,不是风格问题。
常见误操作:复制了 GROUP BY 查询的写法,顺手把 OVER 当成修饰词省略;或从文档里复制代码时漏掉了括号。检查执行前先扫一眼括号是否完整。
想算“当前行减全局平均”,别加 ORDER BY
AVG(sales) OVER(ORDER BY id) 看似只是排个序,实际会变成累积平均:第1行是第1条数据的值,第2行是前2条的均值,第3行是前3条……结果完全不是你要的“全表固定均值”。
要广播同一个全局均值到所有行,只用 AVG(sales) OVER() 或等价写法 AVG(sales) OVER(ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)。后者更啰嗦,但意图明确,适合团队协作场景。
- 加了
ORDER BY→ 窗口逻辑自动切换为累积模式 - 没
PARTITION BY也没ORDER BY→ 默认对整张结果集求均值(注意:WHERE 已生效) - WHERE 条件已过滤?那
OVER()算的就是过滤后数据的均值,不是原始全表
差值列容易因浮点精度出问题
比如 sales - AVG(sales) OVER() 算出来本该是 0 的地方显示 -0.0000001,尤其在金额类字段上会影响后续四舍五入或条件判断(如 > 0 判定失败)。
稳妥做法是显式控制小数位:ROUND(sales - AVG(sales) OVER(), 2)。如果业务要求严格零值对齐,可再套一层 COALESCE(ROUND(...), 0) 防 NULL 干扰。
- MySQL 8.0 中
AVG()默认返回DOUBLE,PostgreSQL 返回numeric,类型不一致可能放大浮点误差 - 别依赖数据库自动隐式转换,
CAST(AVG(sales) OVER() AS DECIMAL(10,2))更可控 - 整数列(如
INT类型的 salary)做AVG()时,某些引擎会截断小数,务必乘1.0或显式CAST
子查询模拟窗口函数,性能差且无法分区
有人写 sales - (SELECT AVG(sales) FROM t),逻辑通,但执行代价高:数据库通常会对每一行重复执行该子查询,千万级表上耗时可能是窗口函数的 3~5 倍。
更关键的是,这种写法完全没法扩展——你想按部门算“个人工资 vs 部门均值”?子查询只能写成 correlated 形式,每行触发一次独立扫描,而 AVG(salary) OVER(PARTITION BY dept) 一次扫描完成全部计算。
- 窗口函数在物理层面只遍历源表一次,内部维护累加器
- 子查询即使结果恒定,优化器也不总能识别并缓存,尤其嵌套多层时
- SQLite 用户注意:需 3.25+ 版本且编译时启用窗口函数支持
真正容易被忽略的是:WHERE 条件永远早于窗口函数执行。你写的 WHERE sales > AVG(sales) OVER() 是非法的,因为窗口结果此时还没生成。必须用 CTE 或子查询先算出均值列,再在外层过滤。











