SQL 窗口排名并列处理:row_number、rank 和 dense_rank 怎么选

10-01 4阅读

“取每组前三名”不是一个足够完整的查询需求:是最多三条记录,还是所有占据前三个名次的人,或者前三档不同分数?只要存在并列,三种答案就可能不同。本文以 PostgreSQL 18 为语义基准,使用只读 VALUES 样本,不依赖已有表,也不修改数据。其他数据库的语法和空值默认顺序应另行核对。

SQL 窗口排名并列处理:row_number、rank 和 dense_rank 怎么选

AI生成概念配图:相同成绩共享排名层级,不同成绩进入后续层级。仅作概念说明,不代表实际界面或实测结果。

先把三种排名放在同一组样本里

WITH scores(team, id, name, score) AS (
  VALUES ('A', 1, 'Ada', 100),
         ('A', 2, 'Ben', 90),
         ('A', 3, 'Chen', 90),
         ('A', 4, 'Dee', 80),
         ('A', 5, 'Eva', NULL::integer),
         ('B', 6, 'Faye', 70)
)
SELECT team, id, name, score,
       row_number() OVER (
         PARTITION BY team ORDER BY score DESC NULLS LAST, id
       ) AS rn,
       rank() OVER (
         PARTITION BY team ORDER BY score DESC NULLS LAST
       ) AS rnk,
       dense_rank() OVER (
         PARTITION BY team ORDER BY score DESC NULLS LAST
       ) AS drnk
FROM scores
ORDER BY team, score DESC NULLS LAST, id;

A 组按显示顺序,rn 是一到五,rnk 是一、二、二、四、五,drnk 是一、二、二、三、四。B 组重新从一开始。row_number 为每条记录分配序号;rank 让并列记录共享名次,并在其后留下名次空缺;dense_rank 也共享名次,但下一档紧接着递增。选择哪一种,取决于产品定义,不是谁更高级。

稳定排序与并列定义不要使用同一套字段

示例假设 id 在组内唯一,因此 row_number 在分数相同的情况下仍有确定的先后。rank 和 dense_rank 的窗口排序只放 score,才能把相同分数视作并列。如果为了“让结果稳定”把唯一 id 也加进它们的 ORDER BY,同分记录就不再属于同一组并列,名次会变得像逐行编号一样。

窗口内 ORDER BY 控制计算排名的顺序,不保证最终结果集的展示顺序。最外层仍显式排序,并用 id 打破展示上的并列。这两层排序可以不同:内部表达业务名次,外部表达页面上怎样稳定显示。不要靠数据库当前碰巧返回的顺序决定获奖或分页结果。

排名之后筛选,需要额外一层查询

WITH scores(id, score) AS (
  VALUES (1, 100), (2, 90), (3, 90), (4, 80)
), ranked AS (
  SELECT id, score,
         row_number() OVER (ORDER BY score DESC, id) AS rn,
         rank() OVER (ORDER BY score DESC) AS rnk,
         dense_rank() OVER (ORDER BY score DESC) AS drnk
  FROM scores
)
SELECT * FROM ranked
WHERE drnk <= 3
ORDER BY score DESC, id;

这段独立示例返回四条记录,因为它取前三档分数。改成 rn <= 3="">

同理,放在内部的普通 WHERE 会先排除记录,然后重新对剩余记录排名;放在外层的过滤则保留完整群体计算出的名次。查“某位用户在全组第几名”时,过早只留下该用户,通常会得到第一名。应先确定参与比较的人群,再确定最后需要展示谁。

空值与精度也是排名规则

PostgreSQL 的降序默认把 NULL 放在前面,所以这里明确写 NULLS LAST,让缺少分数的记录排在最后。如果规则要求未评分者不参与,应在排名前排除它们,而不是只改变显示位置。多个 NULL 在上述排名排序中也会成为并列,不能把空值误当作一个真实的最低分。

浮点计算后的微小差异可能让页面看起来同分,数据库却不判为并列。需要按保留位数或分档排名时,应先定义统一的业务分数表达式,再用同一表达式排名与展示。验收至少包含并列跨越截止线、全组同分、空值、单人组和空结果;这里不声称任何性能提升,大表方案还需要实际执行计划与数据分布证据。

参考资料

文章版权声明:除非注明,否则均为云鹊BLOG原创文章,转载或复制请以超链接形式并注明出处。