mysql 8.0+才支持窗口函数,低版本执行row_number()等会报function不存在错误;需用select version()确认版本;云数据库需手动升级;三者排序并列处理不同:row_number()严格递增,rank()并列跳号,dense_rank()并列不跳号;partition by实现组内排名,缺则全表排名;窗口函数结果不能在where或having中过滤,须用子查询或cte。

MySQL 8.0+ 才支持 Window Functions,低版本直接报错
如果你执行 ROW_NUMBER() 或 RANK() 却收到 ERROR 1305 (42000): FUNCTION xxx does not exist,大概率是 MySQL 版本低于 8.0。Window Functions 是 MySQL 8.0 引入的特性,5.7 及更早版本完全不识别这些函数名,也不会降级为普通聚合行为——它就是语法错误。
确认方式很简单:
SELECT VERSION();
返回值必须是 8.0.x 或更高。如果用的是云数据库(如阿里云 RDS、腾讯云 CDB),注意检查实例引擎版本,有些默认仍为 5.7;升级需手动操作,且可能影响兼容性。
ROW_NUMBER()、RANK()、DENSE_RANK() 的行为差异直接影响业务逻辑
三者都按 ORDER BY 排序后编号,但并列时处理不同:相同值是否跳号、是否连续,决定了“第几名”在业务中是否可接受。
-
ROW_NUMBER():严格递增,相同值也分先后(靠隐式行序或ORDER BY后续字段决定),适合唯一流水号场景 -
RANK():并列则同号,之后跳过被占位次(如 1,1,3),适合“并列第一,没有第二”的榜单 -
DENSE_RANK():并列则同号,之后紧接(如 1,1,2),适合需要连续名次的排名(如考试成绩前 10)
示例:对销售额排序
SELECT name, sales, ROW_NUMBER() OVER (ORDER BY sales DESC) AS rn, RANK() OVER (ORDER BY sales DESC) AS rk, DENSE_RANK() OVER (ORDER BY sales DESC) AS drk FROM sales_table;
若两人 sales=100 并列最高,则 rn 是 1 和 2,rk 是 1 和 1,drk 是 1 和 1 —— 下一名 sales=90 时,rk 变成 3,drk 是 2。
PARTITION BY 是实现“组内排名”的关键,漏写就变成全表排名
想按部门分别排销售 Top 3?必须用 PARTITION BY dept。没写就是把整张表当一个分区,结果所有人在一张榜上比,和业务预期完全不符。
常见错误写法:
SELECT dept, name, sales, ROW_NUMBER() OVER (ORDER BY sales DESC) AS wrong_rank FROM sales_table;
正确写法:
SELECT dept, name, sales, ROW_NUMBER() OVER (PARTITION BY dept ORDER BY sales DESC) AS rank_in_dept FROM sales_table;
注意:PARTITION BY 必须出现在 ORDER BY 前;多个分区键用逗号分隔,如 PARTITION BY region, dept;分区字段不要求索引,但若数据量大且频繁查询,建议在 (region, dept, sales) 上建联合索引以加速排序。
WHERE 无法过滤窗口函数结果,HAVING 也不行,得用子查询或 CTE
下面这句会报错:
SELECT *, ROW_NUMBER() OVER (ORDER BY score DESC) AS rnk FROM students WHERE rnk <p>因为窗口函数在 SQL 执行顺序中晚于 <code>WHERE</code>(执行顺序:FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT)。所以 <code>rnk</code> 在 <code>WHERE</code> 阶段还不存在。</p><p>两种安全做法:</p>
- 子查询(兼容所有 MySQL 8.0+):
SELECT * FROM ( SELECT *, ROW_NUMBER() OVER (PARTITION BY class ORDER BY score DESC) AS rnk FROM students ) t WHERE rnk
- CTE(更易读,推荐):
WITH ranked AS ( SELECT *, ROW_NUMBER() OVER (PARTITION BY class ORDER BY score DESC) AS rnk FROM students ) SELECT * FROM ranked WHERE rnk <p>别图省事在外部加 <code>HAVING</code>——它只作用于 <code>GROUP BY</code> 结果,对窗口函数无效。</p><p>复杂点在于:如果窗口函数依赖多层嵌套计算(比如先算人均分再排名),每层都要考虑字段可见性;CTE 虽清晰,但 MySQL 8.0 对嵌套 CTE 的优化有限,超大数据集下性能可能不如物化临时表。</p>











