必须加ORDER BY,否则SUM() OVER(PARTITION BY ...)返回每行均为该分组总和而非累计值;因未排序时“当前行”无定义,窗口默认将整组视为同一逻辑单元累加。

直接用 SUM() OVER(PARTITION BY ... ORDER BY ...),缺 ORDER BY 就不是累计求和,而是整组重复的总和。
为什么 OVER(PARTITION BY ...) 单独用不累计?
因为窗口函数默认的 frame 是 RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW,但这个“当前行”只在明确排序后才有意义。没写 ORDER BY,数据库就认为所有行顺序未定义,于是把整个分组当做一个“当前行集合”,结果每行都显示该分组的总和。
- 错误写法:
SUM(amount) OVER(PARTITION BY dept_id)→ 每行都是该部门总金额 - 正确前提:必须加
ORDER BY(如时间、ID等有业务顺序的字段) - 注意:
ORDER BY列最好有唯一性,否则相同值的行可能被归入同一 frame,导致累计值“跳变”
实际写法与常见陷阱
假设表 sales 有 dept_id、sale_date、amount,想按部门、按日期顺序累计:
SELECT
dept_id,
sale_date,
amount,
SUM(amount) OVER(
PARTITION BY dept_id
ORDER BY sale_date, id -- 加 id 防止同日多笔时排序不确定
) AS cumsum
FROM sales;
- 别漏逗号:PARTITION BY 和 ORDER BY 之间要逗号分隔
- 慎用
ORDER BY sale_date DESC:累计会从最新一笔开始加,逻辑反直觉 - PostgreSQL / SQL Server / Oracle 都支持;MySQL 8.0+ 支持,5.7 不支持窗口函数
想重置累计(比如每月清零)怎么写?
把需要重置的维度放进 PARTITION BY,例如按部门+年月分组:
SUM(amount) OVER( PARTITION BY dept_id, YEAR(sale_date), MONTH(sale_date) ORDER BY sale_date, id )
- 不能只靠
WHERE过滤来“模拟”重置——那只是筛数据,不是窗口逻辑重置 - 日期函数注意兼容性:
YEAR()/MONTH()在 MySQL/SQL Server 可用,PostgreSQL 要用EXTRACT(YEAR FROM ...) - 如果用字符串拼接生成分区键(如
CONCAT(YEAR(...), '-', LPAD(MONTH(...),2,'0'))),确保无歧义且索引友好
真正容易被忽略的是 ORDER BY 的语义强度——它不只是排序,它定义了“累计”的路径。哪怕业务上觉得“顺序无所谓”,数据库也必须知道哪一行该先加、哪一行该后加,否则累计就失去意义。











