默认帧是什么
窗口函数有没有 ORDER BY,决定了它的默认帧:没有 ORDER BY 时帧是整个分区;有 ORDER BY 时帧是从分区起点到当前行(RANGE UNBOUNDED PRECEDING AND CURRENT ROW,按 ORDER BY 值并列的行会一起算)。MySQL 8.0 与 PostgreSQL 都遵循这套 SQL 标准默认,所以「加了 ORDER BY 之后合计值变成累计值」不是 bug。
基本信息
| 适用平台 | MySQL 8.0+ / PostgreSQL |
|---|---|
| 无 ORDER BY | 帧 = 整个分区 |
| 有 ORDER BY | 帧 = 分区起点到当前行(含并列行) |
| 显式控制 | ROWS/RANGE BETWEEN … AND … 覆盖默认帧 |
| 受影响的函数 | SUM、AVG、COUNT、MIN、MAX、FIRST_VALUE、LAST_VALUE 等 |
典型使用场景
- 累计求和(running total):按时间排序逐行累加
- 移动平均:只看前后 N 行做平滑
- 组内占比:当前行值除以整组总和
- 取组内首值/末值:配合 FIRST_VALUE、LAST_VALUE
- 排名与序列:ROW_NUMBER、RANK 不受帧影响,但常与帧同时出现
两种默认与显式帧
两种默认与显式帧
-- 无 ORDER BY:整分区求平均,每行结果相同
SELECT id, dept_id, AVG(salary) OVER (PARTITION BY dept_id) AS dept_avg
FROM employees;
-- 有 ORDER BY:默认 RANGE 帧,累计到当前行
SELECT id, dept_id, salary,
SUM(salary) OVER (PARTITION BY dept_id ORDER BY hired_at) AS sum_so_far
FROM employees;
-- 显式整分区帧:逐行都拿到组内总额
SELECT id, SUM(salary) OVER (ORDER BY hired_at ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS dept_total
FROM employees;常用帧写法对照
| 帧写法 | 范围 | SUM 的行为 |
|---|---|---|
| OVER() | 整个分区 | 组内总和,逐行相同 |
| OVER(ORDER BY t) | 起点→当前行(含并列) | 累计到当前行的合计 |
| ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING | 整个分区 | 组内总和,逐行相同 |
| ROWS BETWEEN 2 PRECEDING AND CURRENT ROW | 前三行滑动窗口 | 三行移动求和 |
| ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING | 当前行→组末 | 反向累计 |
RANGE 按 ORDER BY 的值划界,并列值一起算;ROWS 按物理行数划界。有并列值时两者结果不同,需要逐行差异就用 ROWS。帧写窄了首尾几行的窗口会被截断,统计口径要在报表里注明。
窗口聚合不会把多行压成一行:结果行数与原表一致,适合和明细并排展示;需要汇总成一行时用 GROUP BY。窗口函数在 WHERE 之后求值,所以 WHERE 里不能直接引用窗口函数或其别名,想过滤要外套一层子查询。
累计值在分组首行等于该行自身。若报表要的是「不含当前行」的前序累计,帧要写成 ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING;差一行就对不上的对账问题多来自这里。