导读:本期聚焦于陈远山创作的《SQL窗口函数ROWS BETWEEN怎么用?固定窗口聚合计算详解》,敬请观看详情。ROWS BETWEEN是SQL窗口函数中定义固定行数窗口的关键语法,常用于移动平均、累计求和、滑动统计等场景。本文从窗口函数的基本结构入手,详细讲解ROWS BETWEEN UNBOUNDED PRECEDING、CURRENT ROW以及N PRECEDING等边界写法的含义与区别,对比ROWS与RANGE两种窗口模式的差异,并通过销售数据实例演示三日均销量、近N条记录聚合等典型计算方法,同时总结使用中的排序依赖、NULL处理与性能优化要点,帮助读者熟练掌握固定窗口下的聚合计算技巧。

在做数据分析时,我们经常遇到这类需求:计算每个用户最近3笔订单的平均金额、统计每7天的滑动销量、或者求每个商品当前记录与上一条记录的差值。这些需求的共同特点是聚合范围不是整个分组,而是一个以当前行为中心的固定行数窗口。普通的GROUP BY无法胜任这类计算,因为GROUP BY会把多行压缩成一行,而窗口函数可以在保留每一行的同时进行聚合,其中ROWS BETWEEN子句正是用来精确控制这个窗口边界的关键语法。

SQL窗口函数ROWS BETWEEN怎么用?固定窗口聚合计算详解

一、窗口函数的基本结构与ROWS BETWEEN的位置

要理解ROWS BETWEEN,先要清楚窗口函数的完整语法结构。一个典型的窗口函数调用包含三部分:函数名(如SUM、AVG、COUNT、MAX)、OVER子句,以及OVER内部的三个可选部分——PARTITION BY分区、ORDER BY排序、窗口框架FRAME。ROWS BETWEEN就属于最后一部分,它定义了相对于当前行,聚合计算要覆盖哪些行。

完整语法如下:

SELECT
    SUM(amount) OVER (
        PARTITION BY product_id          -- 分区:按商品划分
        ORDER BY sale_date               -- 排序:决定行的先后顺序
        ROWS BETWEEN 2 PRECEDING AND CURRENT ROW  -- 框架:当前行及前面2行
    ) AS moving_sum
FROM sales;

这段代码中,ROWS BETWEEN 2 PRECEDING AND CURRENT ROW表示窗口从当前行往上数2行开始,一直到当前行结束,共3行参与求和。ROWS BETWEEN的起点和终点各有多种写法,常用的组合包括:

  • UNBOUNDED PRECEDING:分区内第一行,也就是从最开头算起
  • N PRECEDING:当前行之前的第N行
  • CURRENT ROW:当前行本身
  • N FOLLOWING:当前行之后的第N行
  • UNBOUNDED FOLLOWING:分区内最后一行

需要注意的一个细节是,当ORDER BY子句存在而未显式指定框架时,很多数据库(如PostgreSQL、SQL Server)的默认框架是RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW,而不是很多人以为的ROWS形式。这两者在排序值存在并列时结果会不一样,这是实际使用中最容易踩的坑之一,后面会详细对比。

二、典型应用场景:滑动平均与累计求和

以一张销售明细表sales为例,表结构为(id, product_id, sale_date, amount),我们来看看ROWS BETWEEN能解决哪些实际问题。

第一个场景是计算三日均销量。电商运营中经常需要观察销量的短期趋势,消除单日波动带来的干扰,滑动平均是最常用的指标:

SELECT
    product_id,
    sale_date,
    amount,
    ROUND(AVG(amount) OVER (
        PARTITION BY product_id
        ORDER BY sale_date
        ROWS BETWEEN 2 PRECEDING AND CURRENT ROW
    ), 2) AS avg_3days
FROM sales
ORDER BY product_id, sale_date;

这里窗口覆盖的是当前行加前面两行,也就是最近三个交易日的数据。如果开头几行不足3行,比如分区的第一行,窗口就只有1行,平均值的分母也相应变小,这是符合预期的自然行为。如果业务上要求必须满3行才计算,可以额外配合COUNT判断行数:

SELECT
    product_id,
    sale_date,
    CASE
        WHEN COUNT(*) OVER w >= 3
        THEN ROUND(AVG(amount) OVER w, 2)
        ELSE NULL
    END AS avg_3days
FROM sales
WINDOW w AS (
    PARTITION BY product_id
    ORDER BY sale_date
    ROWS BETWEEN 2 PRECEDING AND CURRENT ROW
);

第二个场景是累计求和。把窗口起点设为UNBOUNDED PRECEDING,终点设为CURRENT ROW,就能得到从分区开头到当前行的累计值,常用于监控当日累计交易额、进度跟踪等:

SELECT
    product_id,
    sale_date,
    amount,
    SUM(amount) OVER (
        PARTITION BY product_id
        ORDER BY sale_date
        ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
    ) AS running_total
FROM sales;

第三个场景是居中窗口。窗口不必以当前行为终点,例如ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING覆盖前一行、当前行、后一行共3行,适合计算某个值与相邻记录的整体对比,比如检测异常点时看当前值是否明显偏离前后均值。

三、ROWS与RANGE的区别及使用注意事项

ROWS和RANGE是两种窗口模式,写法上只差一个关键字,行为却截然不同。ROWS按物理行数计算窗口边界,RANGE按ORDER BY列的逻辑值计算边界。当排序列存在相同值时,RANGE会把所有与当前行排序值相同的行都纳入窗口,即使它们物理上排在当前行后面。

举个例子,假设表中有三行数据的sale_date都是2024-01-01,金额分别为10、20、30。使用RANGE ... UNBOUNDED PRECEDING AND CURRENT ROW时,这三行的累计结果都是60,因为RANGE认为排序值等于当前值的行都属于窗口范围;而使用ROWS时,三行的累计结果分别是10、30、60。绝大多数滑动窗口场景下,我们想要的是ROWS的物理行行为,所以建议在需要精确控制行数时显式写ROWS。

除了ROWS与RANGE的选择,还有几个实践要点值得注意。第一,ORDER BY不可省略。如果不写ORDER BY,整个分区会被当作一个窗口,ROWS BETWEEN 2 PRECEDING AND CURRENT ROW这样的子句会直接报语法错误,因为窗口框架依赖行的确定顺序。第二,排序的稳定性会影响结果的确定性。当排序键存在并列时,不同数据库甚至同一数据库的不同执行可能得到不同的行顺序,建议在排序列末尾追加一个唯一列(比如自增ID)作为兜底排序,保证结果可复现。第三是性能问题,窗口函数需要对分区内的数据进行排序,数据量大时排序开销明显,合理利用排序列上的索引、减少不必要的分区列,可以显著降低执行成本。

最后提一下兼容性。ROWS BETWEEN在主流数据库中都得到了支持,包括MySQL 8.0及以上、PostgreSQL、Oracle、SQL Server、Hive以及大数据引擎Spark SQL等。如果使用的是MySQL 5.7这样的老版本,窗口函数整体不可用,只能通过自连接或用户变量模拟,写法复杂且性能较差,这种情况下建议升级数据库版本或把计算逻辑上移到应用层处理。

SQL窗口函数ROWS BETWEEN聚合计算修改时间:2026-09-16 05:38:33

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/0916/57750.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。