RANK() / DENSE_RANK() / ROW_NUMBER() 在去重场景下的区别与选择

6次阅读

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

RANK() / DENSE_RANK() / ROW_NUMBER() 在去重场景下的区别与选择

去重时用 RANK() 还是 ROW_NUMBER()?关键看要不要保留名次空隙

直接说结论:如果目标是「取每个分组的第一条且不care名次是否连续」,ROW_NUMBER() 最稳妥;如果想按某字段排序后「保留并列名次、且后续名次跳过」(比如两个并列第1,下一个必须是第3),才用 RANK();而 DENSE_RANK() 是并列后紧接下一名(两个第1,下一个是第2)——但三者本身都不做去重,它们只是给已有行打序号。

真正去重得靠外层 WHEREQUALIFY 配合序号过滤,例如只留 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 不等于适合去重,唯一性才是硬指标。

text=ZqhQzanResources