SQL Server滚动求和必须用SUM() OVER(),因其能保留原始行数并逐行累积;子查询或自连接性能差、难维护,且无法高效支持“截至当天”等业务定义的窗口边界,如UNBOUNDED PRECEDING。

SQL Server 的滚动求和必须用 SUM() OVER(),不能靠子查询或自连接硬算——后者在数据量稍大时就会明显变慢,且逻辑难维护。
为什么 SUM() OVER() 是唯一靠谱的选择
窗口函数能保留原始行数,同时逐行计算累积值;而子查询(比如 (SELECT SUM(price) FROM t WHERE xh )每行都触发一次全表扫描,10 万行就执行 10 万次求和。SQL Server 2005 起就支持完整窗口语法,没必要倒退。
- 聚合函数如
SUM在OVER中默认按ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW行为,但**不能依赖默认**——显式写出更安全 -
ORDER BY列必须有确定顺序:如果排序字段(如order_date)存在重复值,又没加二级排序(如id),SQL Server 可能任意打乱同值行顺序,导致滚动和结果不稳定 - 不加
PARTITION BY就是全局滚动;加了就是“分组内滚动”,比如每个商品单独算累计销量
ROWS 和 RANGE 的实际区别在哪
关键看你要“按物理顺序累加”还是“按时间点合并累加”。SQL Server 只支持 ROWS,不支持 RANGE 配合日期间隔(如 INTERVAL '7 days' PRECEDING),这点和 PostgreSQL/MySQL 8.0+ 不同。
- 用
ROWS BETWEEN 2 PRECEDING AND CURRENT ROW:严格取当前行 + 前两行(共 3 行),不管日期是否相同 - 想实现“最近 7 天滚动和”,SQL Server 里得先生成辅助日期列,再用
JOIN或 CTE 自连接过滤范围,不能靠RANGE -
ROWS下,即使order_date相同,不同行的滚动和也可能不同;而你若真需要“截至该日总和”,就得提前用GROUP BY order_date汇总,再对汇总后结果开窗
常见错误写法与修正
这些写法看着像,但要么错、要么慢、要么结果不可控:
- 漏写
ORDER BY:SUM(amount) OVER()→ 结果是整张表总和,每行都一样,不是滚动 - 只写
ORDER BY date不声明ROWS:虽然 SQL Server 默认行为符合滚动需求,但语义模糊,团队协作时容易误解 - 用
ORDER BY date, id却没确保id唯一:如果id有重复,仍可能产生非确定性排序 - 在
WHERE子句里引用窗口别名(如WHERE cumulative_sum > 1000)→ 报错,窗口函数只能出现在SELECT和HAVING中
真正要命的是:滚动求和的“起点”和“终点”必须由业务定义清楚——是“截至当天”还是“含未来 N 天”,前者用 UNBOUNDED PRECEDING,后者得靠自连接或 CTE 配合日期运算,SQL Server 窗口本身不支持 FOLLOWING 配合日期偏移。这点很容易被忽略,一写就错。

















