MySQL 8.0.2起正式支持窗口函数,低版本报ERROR 1064;ROW_NUMBER()实现严格Top N需显式PARTITION BY和ORDER BY;RANK()跳名次、DENSE_RANK()连续名次,选型取决于并列处理需求。

MySQL 8.0 窗口函数支持情况确认
MySQL 8.0.2 起才正式支持窗口函数,低于该版本会报错 ERROR 1064 (42000)。执行前务必检查版本:
SELECT VERSION();若返回
5.7.33 或更低,ROW_NUMBER()、RANK() 等直接报语法错误,升级是唯一解法。
用 ROW_NUMBER() 实现严格 Top N(每组取前 N 条,不并列)
这是最常用也最可控的方式:按分组排序后编号,再过滤编号 ≤ N 的行。注意 PARTITION BY 和 ORDER BY 必须显式写出,漏掉 PARTITION BY 就变成全表排序了。
示例:查每个部门薪资最高的 3 名员工(无并列)
SELECT dept, name, salary
FROM (
SELECT dept, name, salary,
ROW_NUMBER() OVER (PARTITION BY dept ORDER BY salary DESC) AS rn
FROM employees
) t
WHERE rn <= 3;
-
PARTITION BY dept决定“分组边界”,不是GROUP BY,不能省略 -
ORDER BY salary DESC决定组内排序方向,升序会拿最低的 N 个 - 若存在相同 salary,
ROW_NUMBER()仍强制分配不同序号,结果可能和业务预期不符
用 RANK() 或 DENSE_RANK() 处理并列排名
当需要保留并列时,ROW_NUMBER() 不合适。RANK() 跳过后续名次(如 1,1,3),DENSE_RANK() 则连续(1,1,2)。选哪个取决于业务是否允许“空档”。
示例:查每个部门薪资 Top 3 员工(并列算同一排名)
SELECT dept, name, salary
FROM (
SELECT dept, name, salary,
DENSE_RANK() OVER (PARTITION BY dept ORDER BY salary DESC) AS dr
FROM employees
) t
WHERE dr <= 3;
- 若某部门有 5 人并列第 1 名,
DENSE_RANK()下这 5 人都满足dr = 1,全被查出 —— 可能远超 N 条 -
RANK()在并列后跳位,比如 1,1,3,4,此时RANK() 会漏掉第 4 名但包含第 3 名,语义更模糊 - 没有“自动截断到恰好 N 条”的窗口函数,必须接受并列带来的数量浮动
性能与索引关键点
窗口函数本身不走索引,PARTITION BY + ORDER BY 字段组合是否命中索引,直接影响执行计划。没索引时容易触发 Using filesort 和临时表。
- 最佳索引:复合索引
(dept, salary)(升序)或(dept, salary DESC)(MySQL 8.0+ 支持降序索引) - 如果
ORDER BY含多个字段(如salary DESC, hire_date ASC),索引需完全匹配顺序和方向 - 大表慎用
OVER ()(无PARTITION BY),等价于全表扫描+排序,比子查询还慢
真正难的不是写对语法,而是理解 PARTITION BY 不等于分组聚合、并列处理无法硬控条数、以及索引缺失时性能断崖下跌——这些地方一错,结果或性能就偏得离谱。


















