结论:ROW_NUMBER()用于去重取唯一最新/最优行,RANK()适用于需跳空位的并列排名,DENSE_RANK()适用于不跳空位的紧凑并列排名;三者非替代关系,选错场景会导致数据错误。

直接说结论:不是“该选哪一个”,而是“你要解决什么问题”——三个函数根本不是替代关系,用错场景会出数据事故。
去重取最新/最优一行,必须用 ROW_NUMBER()
常见错误现象:用 RANK() 或 DENSE_RANK() 做“每用户取最新订单”,结果某用户返回 3 条记录,而不是 1 条。
原因很简单:RANK() 和 DENSE_RANK() 对相同排序值(比如同一秒的 created_at)会分配相同序号,WHERE rn = 1 就会捞出多行——这不是 bug,是它设计本意。
实操建议:
- 去重唯一性只靠
ROW_NUMBER()能保证,因为它强制生成连续不重复序号 - 但必须确保
ORDER BY足够稳定:比如ORDER BY updated_at DESC, id DESC,不能只写ORDER BY updated_at DESC - 某些引擎(如 Trino、Spark SQL)在排序字段有大量重复时,
ROW_NUMBER()结果可能因 shuffle 随机性而不可复现
要体现并列排名且跳空位,用 RANK()
典型使用场景:考试成绩单、竞赛名次、KPI 排行榜——大众认知里,“两个第 2 名,下一个就是第 4 名”。
例如成绩表中两个学生都考了 95 分,RANK() 给他们都是 1,下一个 90 分的学生就是 3;而 DENSE_RANK() 会给下一个 90 分的学生标为 2,这不符合颁奖逻辑。
注意点:
-
RANK()的“跳位”是自动发生的,你无法关掉;如果业务要求名次必须连续(比如发奖金按名次段划分),它就不适用 - 它和
DENSE_RANK()一样,不解决去重问题——想“每个科目只取最高分一人”,还得在外层加WHERE+ 主键兜底
要并列但名次不跳,用 DENSE_RANK()
适用场景:内部绩效分级、销售梯队划分、榜单展示(强调紧凑感)——比如“S/A/B/C 级”,两个并列 A 级后,下一个就是 B 级,中间不能空档。
性能与兼容性影响:
- 三者计算开销基本一致,底层都是窗口排序,差异在序号生成逻辑
- MySQL 8.0+、PostgreSQL、SQL Server、Oracle、Trino、Hive 全支持,语法完全统一
- 但注意:
DENSE_RANK()在PARTITION BY内对重复值给相同序号,如果你误把它当ROW_NUMBER()用(比如没加唯一排序字段),照样会取多行
最容易被忽略的一点:这三个函数本身不做任何过滤或删行,它们只是给已有行打标签。真正决定“取哪几行”的永远是外层的 WHERE、QUALIFY 或聚合逻辑——别指望函数自己帮你去重或截断。

















