SQL中SUM OVER计算累计总和的基本写法是SUM() OVER(ORDER BY ...),必须显式指定ORDER BY,否则返回全表总和;若需分组累计,须加PARTITION BY;排序字段含NULL或重复值时应加唯一字段兜底并处理NULL排序。

SQL中SUM OVER计算累计总和的基本写法
直接用 SUM() OVER(ORDER BY ...) 就能算出按指定列排序后的累计和,关键在 ORDER BY 子句必须存在,否则窗口默认是整张表范围,结果就是每行都显示总和,不是“累计”。
常见错误现象:SUM(sales) OVER() 或 SUM(sales) OVER(ORDER BY id ROWS UNBOUNDED PRECEDING) 写法不报错但逻辑不对——前者没排序,后者虽正确但冗余,因为 ROWS UNBOUNDED PRECEDING 是默认行为。
- 必须显式写
ORDER BY,比如ORDER BY order_date或ORDER BY id - 如果排序字段有重复值,累计和会出现“并列跳变”,例如两条同一天的记录,它们的累计值相同,下一条才继续累加
- 想按业务时间+唯一ID双重保序,可写
ORDER BY order_date, id
处理分区(PARTITION BY)时的累计和逻辑
当需要“每个用户各自累计”或“每类产品独立求和”时,必须加 PARTITION BY。它会先分组,再在每组内按 ORDER BY 累计。漏掉 PARTITION BY 是常见错误,会导致跨组污染。
典型错误:写成 SUM(amount) OVER(PARTITION BY user_id ORDER BY created_at) 却忘了 created_at 有 NULL 值——NULL 默认排最前,导致第一条记录的累计值异常偏小甚至为 0。
-
PARTITION BY和ORDER BY可共存,顺序无关,但二者缺一不可才能表达“分组内有序累计” - PostgreSQL/Oracle/SQL Server 都支持;MySQL 8.0+ 支持,5.7 不支持窗口函数
- 若分区字段含大量 NULL,建议提前用
COALESCE(user_id, -1)处理,避免 NULL 被单独分到一组
ORDER BY 字段含NULL时的兼容性陷阱
不同数据库对 ORDER BY 中 NULL 的默认排序方向不一致:PostgreSQL 默认 NULLS LAST,MySQL 8.0 默认 NULLS FIRST,SQL Server 视版本而定。这会导致累计和起始位置错乱。
错误现象:某条记录本该是组内第一条(最早时间),却因 created_at IS NULL 被排到最前,它的累计值只含自己,后续非 NULL 行才开始正常叠加。
- 显式声明
NULLS LAST或NULLS FIRST可控(PostgreSQL/Oracle 支持;MySQL 8.0.22+ 支持;SQL Server 不支持该语法) - 更通用的写法是用
COALESCE(created_at, '1970-01-01')或CASE WHEN created_at IS NULL THEN '1970-01-01' ELSE created_at END - 如果业务上 NULL 代表“未发生”,那它不该参与累计逻辑,应提前过滤或置为极大/极小时间值
性能与索引注意事项
SUM() OVER(ORDER BY ...) 在大数据量下可能变慢,尤其当 ORDER BY 字段无索引时,数据库需临时排序。这不是语法问题,而是执行计划层面的瓶颈。
常见误判:“加了索引就一定快”——其实只有当查询同时满足 WHERE 条件和 ORDER BY 字段能命中同一复合索引时,排序才可能复用索引。
- 推荐为
(partition_col, order_col)建复合索引,例如(user_id, created_at) - 避免在
ORDER BY中使用函数,如ORDER BY DATE(created_at),会导致索引失效 - 在 PostgreSQL 中可通过
EXPLAIN ANALYZE查看是否用了WindowAgg节点及是否触发磁盘排序(Sort Method: external merge Disk)
实际用的时候,最容易被忽略的是 NULL 值排序行为和分区字段的隐式分组边界——这两处一旦出错,累计值看起来“差不多”,但个别行偏差无法归因。










