row_number()才是生成唯一递增序列号的正确函数;rank()和dense_rank()按值分组赋名次,并列时无法区分先后,故不适合做序列号。

RANK() 和 DENSE_RANK() 都不能生成“序列号”——它们生成的是排名,不是唯一递增编号。真要序列号,得用 ROW_NUMBER()。
为什么 RANK() 和 DENSE_RANK() 不适合当序列号用
两者本质是「按值分组赋名次」:只要排序字段值相同,就必然并列,无法区分先后。比如两个学生都考了 95 分:
-
RANK() OVER (ORDER BY score DESC)→ 都返回1,下一个不同分(如 90)直接是3 -
DENSE_RANK() OVER (ORDER BY score DESC)→ 都返回1,下一个不同分是2
但业务常需要“哪怕分数一样,也要编出 1、2、3……”,比如导出名单做抽签编号、分页取唯一行、或对接要求主键顺序的下游系统。这时并列就是硬伤。
真正生成分组内唯一序列号该用 ROW_NUMBER()
ROW_NUMBER() 不看值是否相等,只严格按 ORDER BY 的物理顺序硬编 1、2、3…… 它才是序列号的正确工具。
- 必须带
PARTITION BY才能实现“分组内从 1 开始”:例如按department分组编号 →ROW_NUMBER() OVER (PARTITION BY department ORDER BY hire_date) -
ORDER BY字段必须能保序:如果hire_date有重复,不加兜底字段会导致结果不稳定(SQL Server 尤其明显) - 安全写法是末尾补一个唯一列,比如主键
id:ORDER BY hire_date DESC, id ASC
示例:给每个部门员工按入职时间倒序编号,同天入职者按 id 升序排:
SELECT department, name, hire_date, ROW_NUMBER() OVER (PARTITION BY department ORDER BY hire_date DESC, id ASC) AS seq_no FROM employees;
RANK() 和 DENSE_RANK() 各自该用在什么场景
它们不是 bug,是设计用途不同:
-
RANK()适合强调「层级断层」:比如销售排行榜,两个 Top1 后必须是第 3 名(跳过第 2),体现“并列冠军无亚军”的逻辑 -
DENSE_RANK()适合「顺位分档」:比如按客户年消费额分 1~5 档,两个最高消费客户都是第 1 档,下一个自然就是第 2 档,不能跳 - 误用
DENSE_RANK()做 Top-10 截取时,可能返回 12 行(因并列挤进前 10 名次),而ROW_NUMBER()截取才严格保证 10 行
容易被忽略的关键细节
所有三个函数都依赖 OVER 子句,而 OVER 中的 ORDER BY 必须确定、可复现:
- 聚合后使用窗口函数时,不能在
OVER里再写聚合表达式,比如AVG(salary)—— 必须先在 CTE 或子查询里算好,再对结果集开窗 -
PARTITION BY漏写会变成全表编号,不是每组独立计数 - SQL Server 对非确定性排序更敏感:仅靠
ORDER BY score DESC,相同分的行每次执行可能拿到不同ROW_NUMBER(),线上环境务必加唯一列兜底










