迹忆客 专注技术分享

当前位置:主页 > 学无止境 > 数据库 >

SQL中row_number()、rank()和dense_rank() 函数的区别

作者:迹忆客 最近更新:2023/02/01 浏览次数:

尽管这三个都是 SQL 中的排名函数,在 Microsoft SQL Server 中也称为 Window 函数,但是当在排名上有联系时,即重复记录,rank()dense_rank()row_number() 之间的区别就会出现。 例如,如果按薪水对员工进行排名,那么薪水相同的两名员工的排名是多少? 这取决于使用的排名函数,例如 row_numberrankdense_rank

row_number() 函数总是生成一个唯一的排名,即使有重复的记录,即如果 ORDER BY 子句不能区分两行,它仍然会给他们不同的排名,尽管哪个记录会出现在前面或后面是随机决定的,就像我们的示例 2 员工 ShaneRick 的薪水相同,并且排号为 4 和 5,这是随机的,如果我们再次运行,Shane 可能会排在第 5 位。

rank()dense_rank() 将对无法通过 order by 子句区分的行给予相同的排名,但 dense_rank 将始终生成连续的排名序列,如 (1,2,3,...),而 rank () 会在相同排名的两行或更多行后留下空缺(想想“奥运会”:如果两个运动员获得金牌,就没有第二名,只有第三名)。

令人惊讶的是,所有这些函数在 Microsoft SQL Server 和 Oracle 中的行为都相似,至少在高层是这样,所以如果你在 MSSQL 中使用过它们,你也可以在 Oracle 11g 或其他版本上使用它。


用于构建模式的 SQL

下面是用于创建表并向其中插入一些数据以用于演示目的的 SQL:

IF OBJECT_ID( 'tempdb..#Employee' ) IS NOT NULL DROP TABLE #Employee; 

CREATE TABLE #Employee (name varchar(10), salary int);

INSERT INTO #Employee VALUES ('Rick', 3000);
INSERT INTO #Employee VALUES ('John', 4000);
INSERT INTO #Employee VALUES ('Shane', 3000);
INSERT INTO #Employee VALUES ('Peter', 5000);
INSERT INTO #Employee VALUES ('Jackob', 7000);
INSERT INTO #Employee VALUES ('Sid', 1000);

你可以看到我们包括了两名薪水相同的员工,即 Shane 和 Rick,只是为了演示 SQL Server 中 row_numberrankdense_rank 窗口函数之间的差异,当排名相同时这一点很明显。

Row number vs rank vs dense_rank sql


ROW_NUMBER() 示例

它总是为每一行生成一个唯一的值,即使它们相同并且 ORDER BY 子句无法区分它们。 这就是为什么它被用来解决像我们之前看到的第二高薪水或第 n 高薪水这样的问题。

在下面的示例中,我们有两名薪水相同的员工,即使我们在薪水列上生成了行号,它也会为这两名薪水相同的员工生成不同的行号。

select e.*, row_number() over (order by salary desc) row_number from #Employee e 
result:

name salary row_number
Jackob 7000 1
Peter 5000 2
John 4000 3
Shane 3000 4
Rick 3000 5
Sid 1000 6

你可以在这个例子中看到,我们根据员工的薪水对员工进行排名,即使他们的薪水相同,他们每个人都有一个独特的排名。 ShaneRick 的薪水相同,均为 3000,但他们分别排在第 4 和第 5 位。 值得注意的是,在平局的情况下,排名是随机分配的。


RANK() 示例

rank() 函数会将相同的排名分配给相同的值,即不能通过 ORDER BY 区分的值。 此外,下一个不同的等级不会从紧接的下一个数字开始,但会有一个差距,即如果第 4 名和第 5 名员工的薪水相同,那么他们将拥有相同的等级 4,而薪水不同的第 6 名员工将有一个新的 排名 6。

这是阐明这一点的示例:

select e.*, rank() over (order by salary desc) rank from #Employee e
result:

name salary rank 
Jackob 7000 1 
Peter 5000 2 
John 4000 3 
Shane 3000 4 
Rick 3000 4 
Sid 1000 6

你可以看到 ShaneRick 都排在第 4 位,但是 Sid 排在第 6 位,而不是第 5 位,因为它保持了原来的顺序。


DENSE_RANK() 示例

dense_rank 函数类似于 rank() 窗口函数,即相同的值将被分配相同的等级,但下一个不同的值将具有比前一个等级高一个的等级,即如果第 4 名和第 5 名员工具有相同的等级 salary 那么他们将具有相同的等级,但是具有不同薪水的第 6 名员工将具有等级 5,这与 rank() 函数的等级 6 不同。 在 dense_rank() 的情况下,排名不会有差距,如下例所示:

select e.*, dense_rank() over (order by salary desc) dense_rank from #Employee e 
result:
name salary dense_rank 
Jackob 7000 1 
Peter 5000 2 
John 4000 3 
Shane 3000 4 
Rick 3000 4 
Sid 1000 5

我们可以看到 ShaneRick 的排名相同,都是第 4 名,但 Sid 现在排名第 5 名,这与我们在前面的示例中使用 rank() 函数时的第 6 名不同。


row_number 与 rank 与 dense_rank 之间的区别

正如我所说,当存在重复记录时,rankrow_numberdense_rank 之间的差异是可见的。 因为在我们所有的例子中,我们都是根据薪水对记录进行排名,如果两条记录的薪水相同,那么我们会注意到这三个排名函数之间的区别。

row_number 给出了连续的数字,而 rankdense_rank 给出了相同的重复排名,但是排名中的下一个数字是按照连续的顺序排列的,所以你会看到一个跳跃,但在 dense_rank 中排名没有任何差距。

select e.*,
row_number() over (order by salary desc) row_number,
rank() over (order by salary desc) rank,
dense_rank() over (order by salary desc) as dense_rank
from #Employee e

这里的输出清楚地显示了 rank()dense_rank() 函数生成的排名差异。 这将消除大家对 rankdesnse_rankrow_nubmer 函数的疑虑。

Difference between row_number vs rank vs dense_rank in SQL

我们可以看到员工 ShaneRick 的薪水相同,均为 3000,因此当我们使用 rank()dense_rank() 时,他们的排名相同,但下一个排名是 6,这是根据使用 rank() 的连续排名和 5 当我们 使用 dense_rank()row_number() 不会打破联系,并且始终为每条记录提供唯一编号。

顺便说一句,我在 Oracle 11g R2 和 Oracle 12c 上运行了所有三个 SQL 查询,结果相同。 因此,似乎 Oracle 和 SQL Server 都支持这些功能,并且它们的行为相同。

这就是 SQL SERVER 中 ROW_NUMBER()RANK()DENSE_RANK() 函数之间的区别。 正如我所说,差异归结为关系发生的时间。 在平局的情况下,ROW_NUMBER() 会给出唯一的行号,rank 会给出相同的排名,但是下一个不同的rank不会按顺序,会有差距。

dense_rank 的情况下,并列中的两行将具有相同的排名并且不会有差距。 下一个不同的等级将按顺序排列。

转载请发邮件至 1244347461@qq.com 进行申请,经作者同意之后,转载请以链接形式注明出处

本文地址:

相关文章

使用 Mysqldump 备份 MySQL 中的数据

发布时间:2023/05/09 浏览次数:192 分类:MySQL

本篇文章将介绍如何使用 mysqldump 只备份数据。 在这里,我们将探讨 --no-create-info 、--compact 、--skip-triggers 和 --no-create-db 选项。

更新 MySQL 表中的主键

发布时间:2023/05/09 浏览次数:61 分类:MySQL

本篇文章介绍如何更新 MySQL 表中的主键。 我们将使用 ALTER 命令对主键进行任何更改。更新 MySQL 表中的主键 我们可以在多种情况下更新 MySQL 表中的主键。

在 MySQL 中获取命令历史记录

发布时间:2023/05/09 浏览次数:150 分类:MySQL

本文重点介绍了在 Windows 和 Linux 中获取我们已执行的 MySQL 命令历史记录的各种方法。MySQL命令历史

Oracle 的 decode 函数在 MySQL 中的等价物

发布时间:2023/05/09 浏览次数:115 分类:MySQL

本篇文章介绍了三种替代实现,我们可以将它们用作 MySQL 中 Oracle 的 decode() 函数的等价物。 为此,我们将使用 IF()、CASE 以及 FIELD() 和 ELT() 的组合。

在 Linux 中安装 MySQL 客户端

发布时间:2023/05/09 浏览次数:72 分类:MySQL

在 Linux 中安装 MySQL 客户端的命令。Linux 和 Unix 等环境作为命令行界面工作,仅在命令的帮助下运行。

在 MySQL 中转换为十进制

发布时间:2023/05/09 浏览次数:150 分类:MySQL

有时,我们可能需要将一种数据类型转换为另一种数据类型。 下面是我们如何使用带有 DECIMAL(M,D) 的 CAST() 和 CONVERT() 函数在 MySQL 中转换为十进制。

在 MySQL 中获取当前日期和时间

发布时间:2023/05/09 浏览次数:145 分类:MySQL

本篇文章我们将学习 NOW()、CURRENT_TIMESTAMP()(也写为 CURRENT_TIMESTAMP)和 SYSDATE() 来获取 MySQL 中的当前日期和时间。 我们还将看到这三个功能之间的比较。在 MySQL 中获取当前日期和时间

更改 MySQL 服务器中的 max_allowed_packet Size

发布时间:2023/05/09 浏览次数:142 分类:MySQL

本篇文章介绍如何更改 MySQL 服务器中的 max_allowed_packet 大小。 为了了解这一点,我们将使用两个操作系统,Windows 10 和 Linux (Ubuntu)。

扫一扫阅读全部技术教程

社交账号
  • https://www.github.com/onmpw
  • qq:1244347461

最新推荐

教程更新

热门标签

扫码一下
查看教程更方便