窗口帧看哪些行
窗口函数默认看的是「从头到当前行」这个范围,范围本身是可以指定的,这就是窗口帧。帧有两种定义方式:按物理行数偏移,或者按排序列的取值区间偏移。两者在排序值唯一时结果一致,一旦出现并列值就分道扬镳——按取值算的帧会把所有并列行一起纳入。这也是同一段 SQL 在不同数据分布下算出不同累计值的根源。
基本信息
| 适用平台 | MySQL 8.0 / PostgreSQL 12 及以上 |
|---|---|
| 默认帧 | 带排序的聚合窗口函数默认从起始行到当前行 |
| 两种模式 | 按行数偏移与按取值区间偏移,并列值处理不同 |
| 常用边界 | 无界前导、向前若干行、当前行、向后若干行、无界后随 |
| 取值偏移前提 | 排序列必须支持算术运算,字符串列通常不可用 |
典型使用场景
- 近若干天滑动平均:按日期排序后取固定行窗口
- 逐行累计:从起算点累加到当前行的金额或数量
- 同行对比:拿当前行与前后各一行做差值,看环比变化
- 组内累计占比:先算组内累计,再除以组内合计
- 榜单分档:在窗口内做排名后再取固定条数
两种帧的对比
两种帧的对比
-- 按行数算:严格最近两行(前一行加当前行)
SELECT dt, sales,
SUM(sales) OVER (ORDER BY dt
ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) AS s_rows
FROM daily_sales;
-- 按取值算:同一天有多行时,这几行会被整组纳入帧
SELECT dt, sales,
SUM(sales) OVER (ORDER BY dt
RANGE BETWEEN 1 PRECEDING AND CURRENT ROW) AS s_range
FROM daily_sales;
-- 逐行累计:从起始行加到当前行
SELECT dt, sales,
SUM(sales) OVER (ORDER BY dt
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS running_total
FROM daily_sales;帧写法与用途
| 帧定义 | 含义 | 典型用途 |
|---|---|---|
| 按行偏移 | 按物理行数确定范围 | 滑动平均、前后行对比 |
| 按取值偏移 | 按排序列取值区间确定范围 | 日期区间累计,注意并列值 |
| 无界前导到当前行 | 从起始行累计到当前行 | 累计值、组内累计占比 |
| 当前行到无界后随 | 从当前行向后全部 | 剩余量、倒序累计 |
| 省略帧子句 | 聚合窗口函数默认从起始到当前行 | 逐行累计类需求 |
按取值偏移时,数值表示与排序列取值的距离,因此要求排序列能做加减运算。日期与时间戳可以配合区间写法使用,字符串列通常不行,会直接报错。
做逐行累计时优先用按行数偏移:同一天存在多条记录时,按取值算出的累计会让这几条记录互相「看得见」,得到相同的累计值,与逐行累计的预期不符。