首页/数据/数据库速查/窗口函数:ROW_NUMBER / RANK / SUM OVER(8.0)
SQL 语法52 条

窗口函数:ROW_NUMBER / RANK / SUM OVER(8.0)

在结果集上做排名、累计与移动聚合而不折叠行;比自连接写法更清晰也更快,是 8.0 升级的重要理由。

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

窗口函数是什么

窗口函数在保留原始行的前提下做排名、累计与相邻行取值:它按分区切分数据、按排序定义顺序,然后把计算结果附加到每一行,而不像分组聚合那样把多行折叠成一行。MySQL 8.0 起支持,是升级到 8.0 的重要理由之一。

基本信息

版本要求MySQL 8.0 及以上,5.7 及以下不识别语法
排名类ROW_NUMBER()、RANK()、DENSE_RANK()、NTILE()
取值类LAG()、LEAD()、FIRST_VALUE()、LAST_VALUE()
聚合类SUM/AVG/COUNT/MAX/MIN 后加 OVER 子句
子句结构OVER (PARTITION BY 分组列 ORDER BY 排序列 帧定义)

典型使用场景

  • 分组取最新一条:按用户分区、按时间倒序编号,取编号为 1 的记录
  • 排行榜并列处理:需要名次不跳号用密集排名,需要跳号用普通排名
  • 同比环比:用 LAG 或 LEAD 取上一行或下一行的值直接算差值
  • 累计与移动平均:按时间排序做累计求和或滚动窗口平均
  • 去重取一条:同一业务键有多条记录时,按规则编号后只保留第一条

常用写法

常用写法
-- 每个用户取最近一单
SELECT * FROM (
  SELECT o.*, ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at DESC) AS rn
  FROM orders o
) t WHERE rn = 1;

-- 累计金额与环比
SELECT id, amount,
  SUM(amount) OVER (ORDER BY id) AS cum_amount,
  LAG(amount) OVER (ORDER BY id) AS prev_amount
FROM orders;

排名函数对照

函数并列值处理名次序列示例
ROW_NUMBER并列也给不同编号1 2 3 4
RANK并列同名次,后续跳号1 1 3 4
DENSE_RANK并列同名次,后续不跳号1 1 2 3
NTILE(n)按顺序切分成 n 段均匀分桶,用于分位数
窗口函数不能写在 WHERE 子句里:执行顺序在条件过滤之后,必须用外层子查询或公共表表达式先算出编号再过滤。
没有 ORDER BY 时窗口覆盖整个分区,结果与分区聚合相同;加上 ORDER BY 后默认帧是从分区开头到当前行,累计值会随行推进。
在低版本实例上执行窗口语法会直接报语法错误,升级前先用 SELECT VERSION() 确认版本。

改写对照

  • 分组取最新一条:旧写法是自连接求最大值,新写法是分区编号后取编号为 1
  • 累计求和:旧写法依赖变量逐行累加,顺序不保证时结果会错,窗口函数无此问题
  • 同环比:旧写法靠自连接错位关联,窗口函数用取上一行函数一次搞定
  • 去重保留一条:旧写法用临时表加分组,新写法在外层过滤编号

易错点

  • 把窗口函数写进 WHERE:语法被拒绝或语义不符预期,必须改用外层子查询过滤
  • 混淆 ROW_NUMBER 与 RANK:并列值时一个不重复、一个跳号,取「每组一条」必须用前者
  • 忽略默认帧的影响:加 ORDER BY 后求和变成累计,想要全分区合计需要显式指定帧
  • 在无索引的大表上做窗口排序:排序本身开销很大,先确认排序列可被索引支撑

数据来源与参考

  • MySQL 8.0 Reference Manual: Window Functions

相关工具

​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌