mysql 8.0中row_number()必须搭配over子句使用,且需显式指定order by(partition by可选);因窗口函数结果不能在where中直接引用,须用子查询或cte先计算再过滤。

MySQL 8.0中ROW_NUMBER()必须搭配窗口定义使用
直接写 ROW_NUMBER() 会报错,因为 MySQL 要求每个窗口函数都显式声明 PARTITION BY 和 ORDER BY。不加 OVER 子句是语法错误,不是功能缺失。
常见错误现象:ERROR 1064 (42000): You have an error in your SQL syntax,往往是因为漏了 OVER (...) 或括号不匹配。
-
PARTITION BY决定“分组依据”,比如按category分组就写PARTITION BY category -
ORDER BY决定组内排序逻辑,必须存在,且建议加上DESC(否则默认升序,TopN 就变成 BottomN) - 如果只想要全局 TopN(不分组),
PARTITION BY可以省略,但OVER(ORDER BY score DESC)仍不可少
分组 TopN 的标准写法:子查询套一层过滤
MySQL 不支持在 WHERE 中直接引用窗口函数结果,所以必须用派生表(子查询)或 CTE 把 ROW_NUMBER() 的结果先算出来,再筛 rn 。
典型场景:查每个部门薪资最高的 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
- 别名
t是必须的,否则外层SELECT会报错Unknown column 'rn' - 如果允许并列(比如相同 salary 算同一排名),改用
RANK()或DENSE_RANK(),但它们不叫 “TopN” 而是 “TopN with ties” - 性能上,
ROW_NUMBER()本身不触发额外排序——只要ORDER BY字段有索引,执行计划里通常只显示一次 filesort
ORDER BY 字段没索引时性能会明显下降
当 PARTITION BY dept ORDER BY salary DESC 中的 salary 列没有索引,MySQL 会在临时表中做全量排序,数据量大时延迟陡增。
- 验证方式:对查询加
EXPLAIN FORMAT=TREE,看是否有Using temporary; Using filesort - 优化手段:给组合索引
(dept, salary)(注意顺序:先PARTITION BY字段,再ORDER BY字段) - 如果
dept值很多、每组数据极少,单列salary索引也可能被用上,但不如联合索引稳定
WHERE 和 ROW_NUMBER() 的执行顺序容易搞反
很多人想“先 WHERE 再分组”,却把条件写在外层,导致逻辑错误。例如:只查 salary > 5000 的人中的 Top3 —— 这个条件必须放在子查询里,而不是外层 WHERE。
SELECT dept, name, salary
FROM (
SELECT dept, name, salary,
ROW_NUMBER() OVER (PARTITION BY dept ORDER BY salary DESC) AS rn
FROM employees
WHERE salary > 5000 -- ✅ 过滤必须在这里
) t
WHERE rn <p>如果把 <code>salary > 5000</code> 放到外层,等于先取每组 Top3,再从中筛高薪者,结果可能为空,或者漏掉本该进 Top3 的人。</p><p>真正难处理的是动态 N(比如每个部门 TopN 不同),这时得用关联子查询或 LATERAL(MySQL 8.0.14+),但那就超出 <code>ROW_NUMBER()</code> 的适用边界了。</p>











