SQL累计求和不能直接用SUM(),因它是聚合函数会压缩成单行;需用窗口函数SUM() OVER,关键要写全ORDER BY(确保有序)、窗口范围(默认UNBOUNDED PRECEDING TO CURRENT ROW)和PARTITION BY(分组时)。

SQL累计求和为什么不能直接用SUM()?
因为 SUM() 是聚合函数,单独使用会把整张表聚合成一行,丢失原始行级结构。要做“每行显示截至当前的累计值”,必须保留原有行,并按指定顺序逐行累加——这正是窗口函数的典型场景。
怎么写SUM OVER才能得到正确累计和?
核心是明确三要素:排序依据、窗口范围、是否分区。漏掉或写错任意一项,结果就可能全错。
-
ORDER BY必须存在且逻辑合理(比如按时间、ID升序),否则累计无意义; - 默认窗口范围是
UNBOUNDED PRECEDING TO CURRENT ROW,即从第一行累加到当前行——这个隐式行为要心里有数; - 如果需分组累计(如每个用户各自累计订单金额),必须加
PARTITION BY user_id,否则所有数据混在一起算; - 避免在
OVER子句里写ORDER BY同时又在外部ORDER BY,可能导致执行计划混乱或结果错位。
常见错误现象和排查点
实际写的时候,最常遇到的是“累计值全一样”或“累计值跳变不连续”,基本都出在这几处:
- 忘记写
ORDER BY—— 数据无序,窗口默认按物理存储顺序处理,结果不可控; - 排序字段有重复值且未加唯一键(如多个订单同一天),会导致“相同排序值的行共享同一累计值”,解决办法是在
ORDER BY末尾补一个唯一字段,例如ORDER BY order_date, order_id; - 误把
PARTITION BY当成GROUP BY使用,结果行数变少,其实是窗口没生效; - 在 MySQL 8.0 之前版本直接写
SUM() OVER会报错ERROR 1064,得换变量模拟或升级版本。
一个带业务含义的实操示例
假设有一张销售记录表 sales,字段为 sale_id、product、amount、sale_date,想按日期顺序计算每日销售额累计值:
SELECT sale_id, product, amount, sale_date, SUM(amount) OVER (ORDER BY sale_date, sale_id) AS cum_amount FROM sales;
注意这里用了 sale_date, sale_id 双排序,防止同天多笔销售被合并累计;cum_amount 是别名,不是列名,不能在 WHERE 或 HAVING 中直接引用——这是窗口函数的执行时机决定的,容易忽略。










