去重应优先用 ROW_NUMBER(),因其生成唯一序号可确保每组仅取一行;RANK() 和 DENSE_RANK() 用于体现并列排名,不适用于去重。

去重时用 RANK() 还是 ROW_NUMBER()?关键看要不要保留名次空隙
直接说结论:如果目标是「取每个分组的第一条且不care名次是否连续」,ROW_NUMBER() 最稳妥;如果想按某字段排序后「保留并列名次、且后续名次跳过」(比如两个并列第1,下一个必须是第3),才用 RANK();而 DENSE_RANK() 是并列后紧接下一名(两个第1,下一个是第2)——但三者本身都不做去重,它们只是给已有行打序号。
真正去重得靠外层 WHERE 或 QUALIFY 配合序号过滤,例如只留 ROW_NUMBER() = 1 的行。
为什么 DENSE_RANK() 在「取最新一条」时容易出错
DENSE_RANK() 对相同排序值会分配相同序号,但它的设计初衷是「紧凑排名」,不是「唯一标识」。当排序字段存在重复值(比如多条记录的 updated_at 完全相同),DENSE_RANK() 会把它们全标为 1,外层过滤 WHERE rn = 1 就可能返回多行——这不是 bug,是它本意如此。
常见误用场景:
- 按
user_id分组 + 按created_at降序排,想取最新一条,却用了DENSE_RANK() OVER (PARTITION BY user_id ORDER BY created_at DESC) - 多个记录
created_at相同 → 全得rn = 1→ 去重失败
解决办法:在 ORDER BY 后加唯一字段兜底,例如 ORDER BY created_at DESC, id DESC,再配合 ROW_NUMBER() 才能确保每组只出一行。
ROW_NUMBER() 是去重最常用的函数,但要注意排序稳定性
ROW_NUMBER() 一定生成唯一序号,所以 WHERE rn = 1 能稳定取到「每组一行」。但它不承诺哪一行被标为 1——取决于 ORDER BY 是否能完全区分所有行。
实操建议:
- 排序字段必须包含足够区分度,否则结果不可预测(尤其在分布式引擎如 Spark / Trino 中)
- 推荐写法:
ORDER BY score DESC, id ASC(用主键或唯一字段收尾) - 避免只写
ORDER BY score DESC,尤其是 score 有大量重复时 - 某些数据库(如 MySQL 8.0+)对无明确排序的
ROW_NUMBER()会报错或警告
RANK() 和 DENSE_RANK() 真正该用在哪
它们适合需要「体现并列关系」的报表或业务逻辑,比如榜单、绩效分级、考试排名——而不是用来去重。
典型场景举例:
- 学生成绩榜:两个学生都考了 95 分,应同为第 1 名 → 用
RANK()(下一个 90 分就是第 3 名) - 销售排行榜:并列后希望名次不跳(95 分两人并列第 1,90 分就是第 2)→ 用
DENSE_RANK() - 所有这些场景,如果后续还要「每组只取一个代表」,仍得额外加规则(比如再按
id取最小)
真正做去重时,别被「RANK」字面意思误导——名字带 rank 不等于适合去重,唯一性才是硬指标。










