必须用dense_rank()实现“相同salary并列且后续不跳号”,rank()并列后跳号(如1,1,3,4),row_number()强制唯一编号;over中order by需显式指定完整排序键以保证确定性;窗口函数优于非等值连接,where须置于窗口外。

SQL Server里rank()和dense_rank()的区别必须分清
直接说结论:如果你要实现“相同salary并列、后续不跳号”,用 dense_rank();如果要“并列后跳号”(比如两个第一,下一个就是第三),才用 rank()。很多人写错就卡在这一步,结果排名序列对不上业务要求。
常见错误现象是:查出来两个并列第1,但下一个显示第3而不是第2——那是用了 rank() 却误以为它该连续编号。实际 dense_rank() 才对应“并列不跳号”的直觉逻辑。
-
row_number()强制唯一编号,哪怕 salary 完全一样也会给 1/2/3 -
rank()并列时同号,但会跳过后续编号(1,1,3,4) -
dense_rank()并列时同号,后续紧接(1,1,2,3)
over(order by ...)里必须加明确排序字段,不能只靠where过滤
比如想按 salary desc 排名,但又要求 salary 相同时按 emp_no asc 稳定排序——这个 emp_no 必须显式写进 order by 子句里,否则 SQL Server 可能返回非确定性结果。
错误写法:dense_rank() over (order by salary desc) —— salary 相同时,emp_no 顺序由引擎决定,每次执行可能不同。
正确写法:dense_rank() over (order by salary desc, emp_no asc)
注意:这里 emp_no 是为了打破 ties,不是为了“二次排序”,而是让整个排序键唯一可重现。
非等值连接不是替代窗口函数的首选方案
SQL Server 2005+ 全都支持窗口函数,根本没必要用自连接模拟 dense_rank()。强行用非等值连接(比如 SELECT ..., (SELECT COUNT(DISTINCT s2.salary) FROM salaries s2 WHERE s2.salary > s1.salary) + 1)不仅慢,而且容易漏掉边界情况(如空值、NULL salary)。
性能影响明显:窗口函数是单次扫描,O(n);子查询方式是嵌套循环,O(n²),数据量一过万就肉眼可见变慢。
兼容性上更不用提:SQL Server 2012+ 支持全部标准窗口函数,连 lead()/lag() 都能用,没必要倒退回去写老式自连接。
唯一需要非等值连接的场景,是你在维护一个无法升级的老系统(比如 SQL Server 2000),且明确知道它不支持窗口函数——但这种系统现在极少见。
WHERE条件必须放在窗口函数之外,不能塞进over里
常见错误是把过滤逻辑写成:dense_rank() over (order by salary desc where to_date = '9999-01-01') ——语法直接报错,over 子句里不允许 where。
正确做法是先过滤再开窗:SELECT emp_no, salary, dense_rank() over (order by salary desc, emp_no asc) AS t_rank FROM salaries WHERE to_date = '9999-01-01'
容易踩的坑:如果先开窗再 WHERE,会导致排名基于全表计算,然后才筛出行——排名序号已经错乱了。顺序不能颠倒。
另外,to_date = '9999-01-01' 这种条件常出现在员工薪资快照表中,务必确认该字段类型是 DATE 或 CHAR,避免隐式转换导致索引失效。











