必须用order by hire_date配合rows unbounded preceding,否则sum() over()无法实现按时间顺序的累计;mysql 8.0+必须显式指定该帧,而其他数据库可能默认启用。

SQL里用SUM() OVER()算累计奖金,核心是按入职时间排序
直接给结论:必须用ORDER BY hire_date(或入职时间字段)配合ROWS UNBOUNDED PRECEDING,否则SUM() OVER()默认按逻辑顺序累加,不是按时间先后。很多同学写了OVER(PARTITION BY dept_id ORDER BY hire_date)却没看到“累计”效果,就是因为漏了窗口帧定义,或者排序字段本身有重复值没做二次排序。
- 如果
hire_date有重复(多人同天入职),建议补上ORDER BY hire_date, emp_id避免非确定性结果 -
ROWS UNBOUNDED PRECEDING可省略——当ORDER BY存在且没显式指定帧时,多数数据库(PostgreSQL、SQL Server、Oracle、Doris)会自动采用该默认帧;但MySQL 8.0+必须显式写,否则只算当前行 - 别在
WHERE里过滤掉早期员工再套OVER(),否则累计值从被过滤后的第一条开始算,不是真正“入职以来”
分区(PARTITION BY)加不加,取决于你要“全公司累计”还是“部门内累计”
常见误解是以为“累计”就必须PARTITION BY。其实不需要:不写PARTITION BY就是全表范围累计;加了PARTITION BY dept_id才是每个部门各自从第一个入职员工开始累加。
- 想看每个员工在自己部门的奖金排名+累计值?用
OVER(PARTITION BY dept_id ORDER BY hire_date) - 想看全公司最早入职者到当前员工的总奖金?去掉
PARTITION BY,只留ORDER BY hire_date - 注意:
PARTITION BY字段值为NULL的行会被分到同一组,可能造成意外聚合,建议提前COALESCE(dept_id, 'unknown')
MySQL 8.0+和旧版MySQL处理方式完全不同
MySQL 5.7及更早版本根本不支持OVER(),强行写会报错ERROR 1064。升级到8.0+后,还要注意它默认不启用窗口函数的帧语法简化——也就是不写ROWS子句时,不会自动设为UNBOUNDED PRECEDING,而是等价于ROWS BETWEEN CURRENT ROW AND CURRENT ROW,结果就是每行都只显示自己奖金,毫无累计效果。
- MySQL 8.0+必须显式写:
SUM(bonus) OVER(ORDER BY hire_date ROWS UNBOUNDED PRECEDING) - 测试是否生效:查出两行,
hire_date早的那行sum_bonus应该等于它自己的bonus,晚的那行应该等于两者之和 - 别依赖客户端工具的“自动格式化”,有些工具会把
ROWS UNBOUNDED PRECEDING折叠掉,导致你以为写了其实没生效
奖金字段含NULL时,SUM() OVER()会自动跳过,但得确认业务是否允许
SUM()本身忽略NULL,所以只要bonus字段有空值,对应行不参与累计,后续行的累计值也不会包含它。这通常符合预期,但如果你的业务规则是“未发放=0”,那就得先COALESCE(bonus, 0)再计算。
- 错误写法:
SUM(bonus) OVER(...)→ 遇到NULL直接跳过 - 正确对齐业务:
SUM(COALESCE(bonus, 0)) OVER(...) - 别用
IFNULL或ISNULL混用——不同数据库函数名不同,COALESCE是标准SQL,兼容性最好
ROWS UNBOUNDED PRECEDING。










