首页/数据/SQL 速查/窗口函数的默认帧
聚合与窗口函数54 条

窗口函数的默认帧

有 ORDER BY 时默认帧是 RANGE UNBOUNDED PRECEDING AND CURRENT ROW;没有 ORDER BY 时是整个分区。这解释了很多“结果和想象不一样”。

SQL 速查数据更新于 2026-10-13

默认帧是什么

窗口函数有没有 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;差一行就对不上的对账问题多来自这里。

易错点

  • 写了 ORDER BY 却想要组内总额,拿到的是逐行累计值;去掉 ORDER BY 或显式写整分区帧
  • 不写帧就用 LAST_VALUE,结果只是当前行;取组末要显式 ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
  • 有并列 ORDER BY 值时用默认 RANGE 帧期望逐行累计,并列行会被算成同一个值;改用 ROWS
  • 把窗口函数写进 WHERE 想过滤结果,SQL 会直接报错;需套一层子查询后在外层过滤
  • 帧写成 ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING 时首尾行的窗口被截断,结果与中间行不可比
  • 在子查询里算好窗口值、外层又做 GROUP BY 汇总:窗口列被二次聚合导致数值翻倍,窗口结果要按行使用

数据来源与参考

  • MySQL 8.0 Reference Manual · Window Function Frames
  • PostgreSQL Documentation · Window Function Calls
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌