何时选择rank()而不是dense_rank()或row_number()

问题描述 投票:0回答:4

既然我们可以使用

row_number()
获取分配的行号,并且如果我们想使用
dense_rank()
查找每行的排名而不跳过分区内的任何数字,为什么我们需要
rank()
函数,我可以'不要认为
rank()
函数提供的任何用例是
dense_rank()
row_number()
无法实现的。

是否存在

rank()
最适合的用例?

sql sql-server hive hiveql
4个回答
10
投票

当将

RANK
DENSE_RANK
应用于没有关系的列时,它们都会产生由
ROW_NUMBER
生成的相同序列。
RANK
DENSE_RANK
之间的差异出现在关系的存在中,并且很微妙。考虑下表以及行号、排名和密集排名值:

SALARY | ROW_NUMBER | RANK | DENSE_RANK
1000   | 1          | 1    | 1
1500   | 2          | 2    | 2
1500   | 3          | 2    | 2
2000   | 4          | 4    | 3
2200   | 5          | 5    | 4
2500   | 6          | 6    | 5
2500   | 7          | 6    | 5
2500   | 8          | 6    | 5
3000   | 9          | 9    | 6

希望您可以在上面看到,当出现两条或更多记录的平局时,

RANK
DENSE_RANK
都会为具有相同值的所有记录分配相同的排名。然而,它们的不同之处在于
RANK
继续与
ROW_NUMBER
系列一致的排名计数,而
DENSE_RANK
则不然,而是使用重复排名后的下一个值继续排名计数。

现在回答你的问题,选择

RANK
还是
DENSE_RANK
取决于你的要求。例如,如果您要报告获胜者,并且始终需要报告第一名、第二名和第三名,无论每个名次的平局如何,您都可以使用
DENSE_RANK
。否则,您将使用
RANK
,这可能意味着没有第二名或第三名。如果您确定永远不会有重复项,那么您可以使用
ROW_NUMBER


7
投票

下面的例子应该有助于说明差异。 (下面示例的数据库小提琴链接 - https://dbfiddle.uk/?rdbms=oracle_11.2&fiddle=cef0a4da7559de657aae332491a9c500

CREATE TABLE t AS
SELECT 'p' v FROM dual UNION ALL
SELECT 'p'   FROM dual UNION ALL
SELECT 'p'   FROM dual UNION ALL
SELECT 'q'   FROM dual UNION ALL
SELECT 'r'   FROM dual UNION ALL
SELECT 'r'   FROM dual UNION ALL
SELECT 's'   FROM dual UNION ALL
SELECT 't'   FROM dual;

SELECT
  v,
  ROW_NUMBER() OVER (ORDER BY v) row_number,
  RANK()       OVER (ORDER BY v) rank,
  DENSE_RANK() OVER (ORDER BY v) dense_rank
FROM t
ORDER BY v;
The above will yield:

+---+------------+------+------------+
| V | ROW_NUMBER | RANK | DENSE_RANK |
+---+------------+------+------------+
| p |          1 |    1 |          1 |
| p |          2 |    1 |          1 |
| p |          3 |    1 |          1 |
| q |          4 |    4 |          2 |
| r |          5 |    5 |          3 |
| r |          6 |    5 |          3 |
| s |          7 |    7 |          4 |
| t |          8 |    8 |          5 |
+---+------------+------+------------+

平局的 RANK 和 DENSE_RANK 排名相同,但在平局后的 RANK 中,您将获得排名 并跳过下一个排名。最后以粗体标记的部分是 RANK 和 DENSE_RANK 之间的唯一区别。在 DENSE_RANK 中,数字不会被跳过。

一个很好的类比是,想象一下你们班上有 2 名学生获得了最高的总分。所以,第一名的位置是平的。你必须把一等奖颁给他们俩。现在,总分紧随最高分之后的学生,您将分配给他/她什么位置。如果你问 RANK() 函数 - 它会告诉你第三,因为第一位置被 2 人占据。但是,如果您询问 DENSE_RANK() 函数 - 它会告诉您第二名,因为它不会跳过任何排名,因此在这种情况下,第三名将授予总分紧随第二名的人之后的另一个人。如果您的问题是您的第一名只有一份奖品只有一名获奖者:-) 那么 ROW_NUMBER 将为您解决这个问题,因为 ROW_NUMNER() 会将第一和第二名随机分配给得分最高的学生(某事)就像彩票一样),因此在这种情况下,第三个位置仍然不存在矛盾。

现在来到 RANK 的用例:我知道很多资格考试都使用 RANK() 方法(不一定是数据库中的排名函数,而是相同的算法或方法)来筛选学生数量由于有时竞争可能非常接近,因此平局并不罕见。然而,问题是 - 有一个特定的排名,比如说 50 个作为截止点,但同时他们也有固定的席位数,不能超过。因此,如果出现平局,您必须向两个学生授予相同的排名,因为如果两个学生得分相同,您无能为力,但是下一个排名将被跳过,否则座位将被填满。这类似于足球联赛,在联赛阶段结束时,多支球队的积分相同。现在在这种联赛中,你可以使用其他标准,例如进球数等来消除季后赛的平局,但在检查的情况下,你当然不能通过重新检查来消除平局。这是一个非常实用的用例,说明了这个概念有时是有用的。


1
投票

使用

rank()
比使用
dense_rank
更容易实现查找重复项,因为您只需检查行号何时增加超过 1。

如果使用

dense_rank
,则需要额外的代码。

有趣的是,您可以使用

row_number()
(按要检查重复项的每列的列表进行分区,按要检查重复项的每列的列表进行分区)

但如果您不想检查整行,我只建议使用

row_number
而不是
rank()


0
投票

为了扩展之前所有好的答案,

rank()
显示差距,
dense_rank()
填补它们。前者获得“D”(无奖牌),后者获得第四,则获得“B”(银牌),说一小时内进来(等级 1)或不进来(等级 4,密集等级 2)。这是关于您想要如何填充(或跳过)排名位置或解释值的问题。

© www.soinside.com 2019 - 2024. All rights reserved.