bigywater.tech
MySQL / InnoDB · 86

窗口函数与 frame 语义

用一份含并列值的数据,对比ROWS和RANGE两种窗口frame边界,看它们只有在遇到并列值时才真实浮现出来的差异。

ROWS:逐行递增
10→20→40→60→90,严格按物理行顺序
RANGE:并列行合并
两个并列的10都算出20,两个并列的20都算出60

做的事:一张有并列值的表,同一个边界分别用ROWS和RANGE

代码-- score列:10,10,20,20,30,真实存在并列值 SELECT id, score, SUM(score) OVER (ORDER BY score ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS rows_frame, SUM(score) OVER (ORDER BY score RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS range_frame FROM frame_test;

验证:真实的累计和,在并列值上分道扬镳

实测 · 本机执行输出
id  score  rows_frame  range_frame
1   10     10          20
2   10     20          20   ← 两个score=10的行,range_frame真实相同
3   20     40          60
4   20     60          60   ← 两个score=20的行,range_frame真实也相同
5   30     90          90

rows_frame真实老老实实按物理行的顺序逐行累加:10、20、40、60、90,每一行都比上一行真实多加了自己的score。range_frame真实完全不同:两个score=10的行(id=1和id=2),range_frame真实都是20——因为RANGE的边界不是按"第几行"划的,是按ORDER BY的值划的,所有score值相同的行(这类"值相同的行"在窗口函数里真实叫peer行)会被当成真实同一个逻辑边界,一起被包含进当前frame。

结论:有没有并列值,决定了ROWS和RANGE是不是同一回事

如果ORDER BY的列真实是唯一的(没有并列值,比如按主键或者时间戳精确到微秒排序),ROWS和RANGE在UNBOUNDED PRECEDING AND CURRENT ROW这种边界下会给出完全一样的结果——每一行自己就是唯一的一个"值",peer只有它自己。但一旦真实出现并列值(排名、分数、日期这类字段很常见),两者的差异就会真实浮现:RANGE会把所有并列的行算成同一个中间结果,这正是RANK()和DENSE_RANK()这类排名函数背后真实依赖的逻辑(并列的行应该拿到相同的排名)。窗口函数不指定frame时,MySQL真实默认用的就是RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW,如果需要的是"严格按物理行累加"的效果,必须显式写ROWS。

分步演示:同一份带并列值的数据,两种frame的真实计算过程

每一步都是本文上面用真实窗口函数查询跑出来的原始结果。

并列值出现的那一刻,ROWS和RANGE真实分道扬镳
这一步新验证的frame类型

    本文用到的工具

    • 本机 MySQL 26.7.0(InnoDB),5行数据(含并列score值)的窗口函数对比
    • ROWS BETWEEN ... vs RANGE BETWEEN ...
    京ICP备2026067502号-1京公网安备11010502063487号