选 dense_rank() 还是 rank() 取决于业务语义:需连续名次用 dense_rank(),需体现“击败人数”用 rank();rank() 并列后跳号,查 top n 时可能漏数据,如分数 100,95,95,90 对应名次 1,2,2,4。

选 DENSE_RANK() 还是 RANK(),取决于你是否允许名次断层——要连续档位就用前者,要体现“击败人数”就用后者,不是语法问题,是业务语义选择。
查 Top N 时为什么 RANK() 可能漏数据
RANK() 在并列后跳号,导致名次序列稀疏。比如分数为 100, 95, 95, 90,RANK() 输出 1, 2, 2, 4;执行 WHERE rk 只会返回前三行(名次 1 和两个 2),但名次 3 根本不存在,第四高分(90 分)被跳过。
- 想确保覆盖所有“前三档值”,必须用
DENSE_RANK()——它输出1, 2, 2, 3,WHERE dense_rank 稳定命中全部四行 - 若业务要求“严格最多取 3 条记录”,该用
ROW_NUMBER(),而非任何带并列的函数 -
RANK()的跳号不是 bug,是设计:它统计的是“有多少人分数严格更高”,所以两个 95 分后,下一个是第 4 名(因已有三人分数 ≥ 95)
DENSE_RANK() 怎么避免名次断层
DENSE_RANK() 的逻辑是“值变才加 1”,只要排序字段值相同,名次复用,后续名次紧接。对 [100, 95, 95, 90],它给出 [1, 2, 2, 3],天然适配固定档位划分(如 A/B/C 档对应名次 1/2/3)。
- 错误写法:
RANK() OVER (ORDER BY score DESC)用于分档 → 可能出现1, 1, 3, 4,导致 B 档(rk = 2)查不到数据 - 正确写法:
DENSE_RANK() OVER (ORDER BY score DESC)→ 保证档位编号连续不缺 - 注意:它不合并行,只是打标签;输出仍是原始行数,不是聚合结果
ORDER BY 写错会让并列逻辑彻底失效
并列判定只看 ORDER BY 最左字段是否相等,后面字段仅用于破 ties(打散顺序),不影响是否并列。
- 错误:
RANK() OVER (ORDER BY sales DESC, customer_count ASC)→ 即使sales相同,customer_count不同也会拆开并列 - 正确:
RANK() OVER (ORDER BY sales DESC) AS rk,再在最终ORDER BY中加sales DESC, customer_count ASC控制展示顺序 - 主排序字段含
NULL时行为不一致:PostgreSQL 默认NULLS LAST,MySQL 不支持该语法,建议显式用COALESCE(sales, 0)
分区排名(PARTITION BY)容易踩的坑
PARTITION BY 是独立重置排名的开关,但很多人忽略它和 ORDER BY 的作用范围关系——RANK() 和 DENSE_RANK() 都只在每个分区内排序,跨区不比。
- 漏写
PARTITION BY→ 全表统一编号,不是“每组内排名” - 分区字段值全相同(如所有
dept_id都是 NULL 或同一值)→ 实际退化为全表排名 - ORDER BY 字段重复且未加唯一键(如只写
ORDER BY score DESC)→ 同分学生每次执行顺序可能不同,导致排名波动;应补上主键或时间戳:ORDER BY score DESC, id ASC
真正难的不是写对函数,而是确认业务到底要“档位连续”还是“层级断层”——一旦选错,下游分页、导出、报表都会无声翻车,而且很难从 SQL 报错里发现。











