窗口函数加OVER是唯一不缩减行数的SQL聚合机制,其核心在于正确使用PARTITION BY、ORDER BY和ROWS/BETWEEN子句,三者组合错误会导致结果偏离业务含义。

窗口函数加 OVER 是唯一能在不缩减行数前提下做聚合的 SQL 机制。 它不是“变通方案”,而是 SQL 标准中专为这种需求设计的语法层级——你要的不是“怎么绕过 GROUP BY”,而是“用对 OVER 的三个关键子句”。
为什么 COUNT(*) 加了 OVER 就不报错也不丢行
因为 COUNT(*) OVER (PARTITION BY dept) 已经脱离聚合函数语义,变成逐行计算:每行都属于某个 dept 分区,函数就在那个分区里数总行数,结果广播回该分区每一行。而 COUNT(*) 单独出现时,SQL 强制要求 GROUP BY 或全表聚合,否则语法非法。
- 没写
PARTITION BY→ 默认全表一个分区,COUNT(*) OVER ()返回每行都是总行数 -
PARTITION BY dept→ 每个部门内部独立计数,员工张三和李四若同属 tech 部,两人所在行的值相同 - 漏掉
OVER括号(如写成COUNT(*) PARTITION BY dept)→ 直接语法错误,不是警告
ORDER BY 在 OVER 里不是可选,而是决定计算逻辑的关键开关
对 SUM()、AVG() 这类聚合型窗口函数,ORDER BY 不写可能不报错,但行为不可控:MySQL 8.0+ 默认用 RANGE 帧,PostgreSQL 默认用 ROWS 帧。同一日期有多条记录时,RANGE 会把当天所有行打包进当前计算,ROWS 只按物理顺序取前 N 行。
- 要累计求和(从分区第一行加到当前行):必须写
ORDER BY date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW - 只按部门分组求平均,不关心顺序:可以省略
ORDER BY,但得确认你用的数据库默认帧类型 -
ROW_NUMBER()、RANK()类排序函数:ORDER BY是强制的,不写直接报错
混合使用窗口函数和普通字段时,最容易踩的坑是作用域混淆
SELECT name, salary, AVG(salary) OVER (PARTITION BY dept), COUNT(*) FROM emp 这种写法在多数数据库里会报错,因为 COUNT(*) 没套 OVER,它仍按传统聚合理解,而 name 和 salary 又不是聚合字段。
- 所有未加
OVER的聚合函数,在 SELECT 中都必须搭配完整GROUP BY - 只要用了任意一个窗口函数,其他字段可以原样保留,无需被分组或包裹
- 想同时输出“部门平均薪资”和“全公司平均薪资”:分别写
AVG(salary) OVER (PARTITION BY dept)和AVG(salary) OVER ()
真正难的不是写对语法,而是判断哪一列该进 PARTITION BY、哪一列该进 ORDER BY、要不要显式声明 ROWS。这三个子句的组合一旦出错,结果可能完全符合语法却严重偏离业务含义——比如本该按时间滚动求和,却因漏写 ROWS 被 RANGE 拉进一堆同天数据,数值突然翻倍。

















