
SUM OVER 累加与 ROWS BETWEEN 窗口边界彻底搞懂滑动窗口的五种语法在日常 SQL 数据分析中求“累计销售总额Running Total”、“移动平滑均值Moving Average”或者“区间最大回撤”是极其高频的统计需求。很多熟练使用SUM() OVER(PARTITION BY ... ORDER BY ...)的工程师在被问到以下细节时往往会语塞为什么不加ROWS BETWEEN时如果排序列有相同的值SUM()会把相同日期的金额一次性全加起来ROWS BETWEEN和RANGE BETWEEN到底有什么本质区别怎么用一条 SQL 算出“包含当天在内过去 7 天的动态滑动窗口销售额”如果对窗口边界Window Frame Clause的底层机制缺乏清晰认知写出的累计指标很容易在遇到并列数据、缺失日期或海量数据聚合时产生隐蔽的统计偏差。今天我们系统拆解OVER()子句中窗口边界定义的完整语法体系与五大实战范式。窗口边界语法的通用骨架一个完整的分析窗口定义由三个部分构成函数名() OVER ( PARTITION BY 分区字段 ORDER BY 排序字段 [ASC|DESC] [ROWS | RANGE] BETWEEN 窗口起始边界 AND 窗口结束边界 )核心关键字字典CURRENT ROW当前行UNBOUNDED PRECEDING分区内的第一行无上界起点UNBOUNDED FOLLOWING分区内的最后一行无下界终点N PRECEDING当前行向前偏移 $N$ 行/单位N FOLLOWING当前行向后偏移 $N$ 行/单位必须掌握的五大经典窗口边界模式假设我们有一张简单的日销售明细表t_sales_daily(dt, amount)。模式一标准累计求和从月初第一天累加到当天SELECT dt, amount, -- 语法 A显式声明物理行边界最安全、最推荐的标准写法 SUM(amount) OVER ( ORDER BY dt ASC ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW ) AS running_total_amount FROM t_sales_daily;模式二滑动居中平均前 1 天 当天 后 1 天共 3 天居中窗口常用于时序数据平滑滤波消除单日随机波动SELECT dt, amount, -- 窗口跨度包含前 1 行、当前行、后 1 行 AVG(amount) OVER ( ORDER BY dt ASC ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING ) AS moving_avg_3d_centered FROM t_sales_daily;模式三回溯滑动窗口包含当天在内的过去 7 天累计业务场景监控任意一天的“过去 7 天滚动总营收”SELECT dt, amount, -- 窗口跨度从前 6 行到当前行共 7 行 SUM(amount) OVER ( ORDER BY dt ASC ROWS BETWEEN 6 PRECEDING AND CURRENT ROW ) AS rolling_sum_7d FROM t_sales_daily;模式四全分区全局聚合同时获取明细与总体占比业务场景在每一行明细旁边直接展示该单金额占全月大盘总金额的百分比无需单独写子查询SELECT dt, amount, -- 全局求和从分区第一行到最后一行 SUM(amount) OVER ( ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING ) AS grand_total_amount, -- 直接计算单日销售贡献占比 ROUND( amount / SUM(amount) OVER (ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) * 100, 2 ) AS contribution_pct FROM t_sales_daily;模式五前向累计从当前天到月底最后一天剩余指标预测业务场景计算当月后续剩余天数的销售目标缺口SELECT dt, amount, -- 从当前行累加到分区最后一行 SUM(amount) OVER ( ORDER BY dt ASC ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING ) AS remaining_month_total FROM t_sales_daily;致命大坑ROWSvsRANGE的暗坑差异这是无数初学者甚至工作三年的老手都会踩翻的经典事故现象实测如果我们在写SUM(amount) OVER (ORDER BY dt)时省略了ROWS BETWEENSQL 标准ANSI SQL默认采用的缺省边界是RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW注意是RANGE而不是ROWS。测试数据 日期 (dt) | 金额 (amount) 2026-09-04 | 100 2026-09-04 | 200 -- 存在相同日期 2026-09-05 | 300如果使用ROWS BETWEEN ...按物理行数累加第 1 行结果100第 2 行结果100 200 300第 3 行结果300 300 600如果不写默认变成RANGE BETWEEN ...按逻辑数值范围累加因为第 1 行和第 2 行的dt值完全相同RANGE认为它们属于同一个“同级数值组Peers”会在这一步直接把相同日期的所有金额全加起来第 1 行结果300提前加了第 2 行第 2 行结果300第 3 行结果600事故后果如果表里有重复排序列省略ROWS BETWEEN会导致中间行的累计值出现虚假的并列跳变完全破坏了按行逐步递增的财务审计要求终极军规永远显式书写ROWS BETWEEN在任何需要严格行级累加的 SQL 中坚决不要依赖默认行为完整敲出ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW。复用命名窗口Window Specification当一个查询中需要同时计算多个相同窗口维度的指标时使用WINDOW w AS (...)提高可读性并提升优化器执行效率SELECT dt, SUM(amount) OVER w AS run_sum, AVG(amount) OVER w AS run_avg, MAX(amount) OVER w AS run_max FROM t_sales_daily WINDOW w AS (ORDER BY dt ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW);