SUM() OVER() 的累计求和本质是按 ORDER BY 定义的行序,在窗口内从首行累加至当前行;必须显式排序,否则报错或结果不可控;重复排序值会导致累计值相同;加 PARTITION BY 可实现分组内独立累计。

什么是 SUM OVER 的累计求和本质
SUM() OVER() 实现累计求和,核心不是“加总全部”,而是靠 ORDER BY 子句定义窗口内行的顺序,并默认使用 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW(即从第一行到当前行)。没写 ORDER BY,SQL 会报错或结果不可控——这是最常被忽略的前提。
- 累计求和必须有明确的排序依据,比如时间戳、序号、ID
- 如果排序字段有重复值,相同值的多行会得到相同的累计值(因为它们在窗口中“并列”)
- 不加
PARTITION BY就是全表累计;加了就是分组内独立累计
如何避免 ORDER BY 相同值导致的累计跳变
当按 created_at 排序,但多条记录时间戳完全相同时,SUM() OVER (ORDER BY created_at) 会让这些行共享同一累计值,后续行直接加上这批的总和,看起来像“跳了一大截”。
- 用复合排序兜底:例如
ORDER BY created_at, id或ORDER BY created_at, ROW_NUMBER() OVER (ORDER BY created_at, id) - 如果业务上允许,给原始数据加唯一序列号(如
ROW_NUMBER() OVER (ORDER BY created_at, id) AS seq),再按seq累计 - 别依赖数据库隐式排序,显式写出所有排序键
SELECT id, amount, created_at, SUM(amount) OVER (ORDER BY created_at, id) AS cumsum FROM orders;
SUM OVER 和子查询自连接累计性能差在哪
手写 (SELECT SUM(t2.amount) FROM orders t2 WHERE t2.id 这类相关子查询,在 10 万行时可能秒级变分钟级;而 SUM() OVER() 是单次扫描 + 窗口滑动,通常快一个数量级以上。
- 窗口函数由优化器统一规划,不产生 N×N 关联
- 大多数主流数据库(PostgreSQL、SQL Server、Oracle、MySQL 8.0+)对
SUM() OVER(ORDER BY ...)有高效实现 - 但注意:如果
ORDER BY字段没索引,排序本身可能成为瓶颈,尤其在无PARTITION BY的全表累计场景
分区累计时 PARTITION BY 和 ORDER BY 的执行顺序
PARTITION BY 先切分数据块,ORDER BY 再在每个块内排序并累计。顺序不能颠倒,也不能只写 ORDER BY 而漏掉 PARTITION BY(否则就是全局累计)。
- 分区字段必须是 GROUP BY 合法字段,不能是表达式(除非数据库支持,如 PostgreSQL 允许
PARTITION BY EXTRACT(YEAR FROM created_at)) - 多个分区键用逗号分隔:
PARTITION BY user_id, status - 分区内累计起点永远是该分区内第一条(按
ORDER BY排序后),不是全表第一条
SELECT
user_id,
order_date,
amount,
SUM(amount) OVER (
PARTITION BY user_id
ORDER BY order_date, order_id
) AS user_cumsum
FROM orders;
累计逻辑看似简单,但实际中最容易栽在排序字段的唯一性、分区边界的理解、以及把窗口函数当成普通聚合来用——它不压缩行数,也不自动去重,只是为每一行计算一个基于窗口的值。










