DENSE_RANK()是唯一满足“相同值并列、后续名次紧接”要求的窗口函数;必须显式用稳定字段(如emp_no或id)补全ORDER BY以确保结果确定;排名计算须在WHERE过滤之前完成,否则档位失真。

必须用 DENSE_RANK(),不能用 RANK() 或 ROW_NUMBER()
要实现“相同值并列、后续名次紧接”,比如 4.00 和 4.00 都是第 1 名,下一个 3.85 就是第 2 名(不是第 3 名),唯一正确的函数是 DENSE_RANK()。用 RANK() 会得到 1, 1, 3, 4,用 ROW_NUMBER() 会得到 1, 2, 3, 4——两者都不满足“并列且不跳号”的业务要求。
ORDER BY 必须显式写全,否则结果不确定
DENSE_RANK() 的排序键必须能唯一确定行顺序,否则 SQL Server 可能每次返回不同排名。常见错误是只写 ORDER BY salary DESC,而 salary 相同的行没有稳定次序。
- 错误写法:
DENSE_RANK() OVER (ORDER BY salary DESC)——salary相同时,emp_no或id的顺序由引擎决定,不可重现 - 正确写法:
DENSE_RANK() OVER (ORDER BY salary DESC, emp_no ASC)——emp_no是“打破并列”的稳定字段,不是为了二次排序,而是让整个ORDER BY键唯一 - 如果原始表没合适字段可用,可加
id(主键)兜底:ORDER BY salary DESC, id ASC
WHERE 过滤不能放在窗口计算之前
如果你先用 WHERE status = 'active' 再算 DENSE_RANK(),那排名只基于过滤后的数据,但业务可能要求“在全部数据中排档位”。例如:某销售员分数排第 2,但若他被 WHERE 过滤掉,其他人就都往前挪一位,导致档位整体上移。
- 错误写法:
SELECT *, DENSE_RANK() OVER (ORDER BY score DESC) FROM sales WHERE status = 'active'—— 排名只反映活跃人员内部顺序 - 正确做法:先算排名,再过滤,或用 CTE 显式分层:
WITH ranked AS ( SELECT *, DENSE_RANK() OVER (ORDER BY score DESC, id ASC) AS drk FROM sales ) SELECT * FROM ranked WHERE status = 'active' AND drk <= 5;
- 注意:
drk别名不能直接在WHERE中引用,因为窗口函数执行晚于WHERE
NULL 值会干扰排名位置,必须显式控制
SQL Server 默认把 NULL 当作最大值(DESC 时排最前),这会让空分被当成“最高分”,挤占真实高分的名次。
- 现象:
score为NULL的记录排第 1,其余全往后错一位 - 安全写法:用
COALESCE(score, -999999)把空值垫底:DENSE_RANK() OVER (ORDER BY COALESCE(score, -999999) DESC) - 更严谨方案:提前清洗或用
CASE WHEN score IS NULL THEN 0 ELSE score END,避免数值语义冲突
真正容易被忽略的是:DENSE_RANK() 的“连续”只针对值变化,不针对行数控制。比如你要取“前 3 名”,但第 3 名有 5 人并列,结果会返回 7 行——这不是 bug,是函数按设计工作的必然结果。是否接受这种行为,得由业务规则决定,而不是靠换函数硬拗。

















