选DENSE_RANK()还是RANK()取决于业务是否需要名次连续:RANK()跳号体现断层(如奖牌榜),DENSE_RANK()连续编号适合档位划分;WHERE rnk=2为空可能因无唯一第二名,属正常设计而非错误。

选 DENSE_RANK() 还是 RANK(),不取决于“哪个更高级”,而取决于你是否需要让名次数字连续——业务上要的是“档位”还是“断层感”。
WHERE rnk = 2 查不到数据?先别改函数,看业务逻辑
常见错误现象:WHERE rnk = 2 返回空,接着怀疑数据漏了、排序错了、甚至重写整个子查询。其实可能只是业务上真没有“唯一第二名”:比如分数为 [100, 100, 95, 90],RANK() 给出 [1, 1, 3, 4],第 2 名根本不存在。
- 这是
RANK()的设计行为,不是 bug:它用跳号表达“前两人并列第一,下一个不同分值直接叫第三名” - 如果业务场景强调层级断层(如奥运奖牌榜、淘汰制绩效档位),这个跳号就是信号,不该被“修复”
- 如果下游系统(比如 BI 看板或 Excel)默认按连续顺位理解排名,那硬用
RANK()就会导致对不上数,沟通成本远高于换函数
取“Top N 档”还是“Top N 行”?目标决定函数
RANK() 和 DENSE_RANK() 都不能直接控制返回行数,但它们对 WHERE 过滤的影响完全不同:
- 用
RANK()做WHERE rnk 可能只返回 5 行(因为名次是 <code>1,1,3,3,5,没凑够 3 个不同名次) - 用
DENSE_RANK()做WHERE dense_rank 可能返回 50 行(只要属于前三档,不管每档多少人) - 想取“销售额最高的前 3 档客户”,必须用
DENSE_RANK();想取“击败至少 90% 用户的 Top 3 层级”,RANK()的跳号反而更准确
ORDER BY 不加兜底字段,结果会飘
无论用哪个函数,只要 ORDER BY 字段有重复值(比如多人同分、同薪资),又没补唯一字段,结果就可能每次执行都不一样:
- 错误写法:
ORDER BY score DESC→ 同为 95 分时,张三和李四谁排前不确定 - 安全写法:
ORDER BY score DESC, id ASC或ORDER BY score DESC, created_at DESC - 注意:
NULL在ORDER BY中的行为因数据库而异:PostgreSQL 默认NULLS LAST,MySQL 8.0 默认NULL排最前,SQL Server 可能按NULLS FIRST—— 显式写NULLS LAST更可控
嵌套子查询不是可选项,是强制步骤
RANK() 和 DENSE_RANK() 是窗口函数,在 SQL 执行顺序中晚于 WHERE,所以以下写法一定报错:
SELECT * FROM sales WHERE RANK() OVER (ORDER BY amount DESC) <= 3;
报错信息通常是 Invalid use of window function 或字段不存在。正确做法只有两种:
- 子查询封装:
SELECT * FROM (SELECT *, RANK() OVER (ORDER BY amount DESC) AS rnk FROM sales) t WHERE rnk <= 3; - CTE 封装:
WITH ranked AS (SELECT *, DENSE_RANK() OVER (ORDER BY amount DESC) AS drk FROM sales) SELECT * FROM ranked WHERE drk <= 3; - 漏掉这层嵌套,是新手踩得最多、报错最直接的坑
真正容易被忽略的点是:函数选错不会让你的 SQL 跑不起来,但会让业务方在导出后反复质疑“为什么 Excel 里算的是第 2 档,这里却是第 3 名”——这种语义偏差,比语法错误更难定位。

















