rank跳空而dense_rank不跳空:rank遇并列时后续名次跳过被占位数(如1,1,3),dense_rank则按不同值档位连续编号(如1,1,2),二者语义分别强调“名次位置”与“名次层级”。

同分时 RANK 和 DENSE_RANK 的行为差异到底在哪
关键区别就一句话:RANK 遇到相同值会“跳空”,DENSE_RANK 则“不跳空”。比如三人得分 100、100、90,RANK 给出的是 1, 1, 3,而 DENSE_RANK 是 1, 1, 2。这不是 bug,是设计意图:前者强调“名次位置”,后者强调“名次层级”。
常见错误是以为两者只是写法不同,结果在报表里出现“第1名、第1名、第3名”却没第2名,业务方直接发问:“第2名去哪了?”——这时候得立刻意识到是用了 RANK 而非 DENSE_RANK。
ORDER BY 必须明确,否则结果不可靠
RANK 和 DENSE_RANK 都是窗口函数,必须搭配 OVER 子句,且 ORDER BY 不可省略。漏写或写成常量(如 ORDER BY 1)会导致排序逻辑失效,排名变成全 1 或随机顺序。
实操建议:
- 始终用具体列名,例如
ORDER BY score DESC,避免ORDER BY 1 - 多列排序要写清楚优先级,比如先按
score DESC,再按name ASC消除并列不确定性 - 如果原始数据有重复
score但没指定第二排序键,不同执行可能产生不同排名顺序(尤其在分布式数据库中)
和 ROW_NUMBER 混用时要注意语义冲突
三者常被一起比较,但语义完全不同:ROW_NUMBER 严格按顺序编号(1,2,3...),不管值是否相同;RANK 和 DENSE_RANK 才真正处理“同分”。混用时容易误以为 ROW_NUMBER 是更“精确”的排名——其实它根本不是排名,只是行号。
典型误用场景:
- 想取“分数最高的前3人”,却用
ROW_NUMBER() OVER (ORDER BY score DESC)+WHERE rn → 可能漏掉并列第3的其他人 - 正确做法是用
DENSE_RANK()或RANK(),再根据业务选“取所有第3名”还是“只取前3个位置” - MySQL 8.0+、PostgreSQL、SQL Server 都支持;旧版 MySQL(
NULL 值怎么排?不同数据库默认策略不一致
NULL 在 ORDER BY 中的默认位置因数据库而异:PostgreSQL 和 SQL Server 默认把 NULL 当最大值(排最后),MySQL 8.0 默认当最小值(排最前)。这直接影响 RANK 结果——比如 score 为 NULL 的记录,在 PostgreSQL 里可能是 RANK = 4,在 MySQL 里可能是 1。
稳妥做法是显式控制:
- 写成
ORDER BY score DESC NULLS LAST(PostgreSQL / Oracle 支持) - MySQL 不支持
NULLS LAST,改用ORDER BY score IS NULL, score DESC - SQL Server 用
ORDER BY score DESC OFFSET 0 ROWS无法控制 NULL,推荐补CASE显式归类
实际查数据前,先 SELECT score, COUNT(*) FROM t GROUP BY score 看看有没有 NULL,比事后调排名逻辑更省时间。










