DENSE_RANK()更适合连续排名,因其并列时后续名次不跳(如1,1,2,3),而RANK()会跳号(如1,1,3,4),业务上要求档位连续、无空缺,且需配合PARTITION BY和ORDER BY确保分组与排序准确。

为什么DENSE_RANK比RANK更适合连续排名
当遇到相同分数、相同销量等并列场景时,RANK() 会跳过后续名次(比如两个第1名后直接第3名),而业务上常要求“两个第1名后是第2名”——这正是 DENSE_RANK() 的设计目的。它只按唯一值分组排序,组内所有行共享同一序号,组间严格递增1。
典型误用是把 DENSE_RANK() 和窗口帧(ROWS BETWEEN)混用:这个函数不支持 ORDER BY ... ROWS 子句,加了会报错 "Incorrect syntax near 'ROWS'"。
正确写法:PARTITION BY + ORDER BY 缺一不可
DENSE_RANK() 必须配合 OVER 子句,且 ORDER BY 是强制项;漏掉会提示 "The function 'DENSE_RANK' must have an OVER clause."。若需分组内独立排名(如按部门排销售Top3),PARTITION BY 不能省略。
常见错误示例:
SELECT name, score, DENSE_RANK() OVER () AS rk -- ❌ 报错:缺少 ORDER BY FROM sales;
正确写法:
SELECT dept, name, score, DENSE_RANK() OVER (PARTITION BY dept ORDER BY score DESC) AS dept_rank FROM sales;
- 多个排序字段时,用逗号分隔:
ORDER BY score DESC, created_time ASC -
PARTITION BY字段值为NULL会被视为同一组,注意清洗或显式过滤 - 排序字段含
NULL时,默认排在最前(ASC)或最后(DESC),SQL Server 不支持NULLS FIRST/LAST
和ROW_NUMBER、RANK的关键行为差异
三者都依赖相同 OVER 结构,但逻辑不同:
-
ROW_NUMBER():严格按顺序编号,相同值也不同号(1,2,3…) -
RANK():并列同号,但跳过后续名次(1,1,3,4…) -
DENSE_RANK():并列同号,后续名次不跳(1,1,2,3…)
例如数据 [100, 100, 95, 90] 排序后:
score | ROW_NUMBER | RANK | DENSE_RANK 100 | 1 | 1 | 1 100 | 2 | 1 | 1 95 | 3 | 3 | 2 90 | 4 | 4 | 3
选哪个取决于业务定义:“并列是否占用名次资源”。促销榜单通常用 DENSE_RANK(),因为用户只关心“我排第几”,不关心前面有多少人并列。
性能与索引建议
DENSE_RANK() 是窗口函数,执行时需对 ORDER BY 字段做完整排序,大数据量下容易成为瓶颈。实际优化点很具体:
- 确保
ORDER BY字段有索引,尤其是复合场景:如PARTITION BY dept ORDER BY score DESC,建索引CREATE INDEX ix_dept_score ON sales(dept, score DESC) - 避免在
SELECT中对DENSE_RANK()结果再套子查询过滤(如WHERE rk = 1),应改用TOP (1) WITH TIES或 CTE + 外层 WHERE - SQL Server 2016+ 支持批处理模式窗口函数,若表启用了列存储索引,性能提升明显
真正卡住的往往不是函数本身,而是没意识到它触发的是全局排序——尤其当 PARTITION BY 组数极少(如全表就3个部门),却要对千万行排序时,DENSE_RANK() 就成了隐性单点瓶颈。

















