MySQL(四):使用SQL实现排名

大家好,我是欧阳方超,可以扫描下方二维码关注我的公众号“欧阳方超”,后续内容将在公众号首发。
在这里插入图片描述
在这里插入图片描述

1、概述

排名的需求总是很常见的,今天就介绍一下MySQL中的rank()函数,如何应用它以将排名分配给结果集的分区中的每一行。

2、rank()函数

rank()函数为结果集每个分区中的每一行分配一个排名,其语法如下:

RANK() OVER (
    PARTITION BY <expression>[{,<expression>...}]
    ORDER BY <expression> [ASC|DESC], [{,<expression>...}]
) 

在这个语法中:
PARTITION BY子句对结果集进行分区。RANK()功能在分区内执行;ORDER BY子句按一个或多个列或表达式对分区内的行进行排序。
注意,over()里头的分组以及排序的执行晚于 where 、group by、 order by 的执行。

2.1、rank()使用

假如有下面一张表,员工所在部门以及工资情况。

idnamedepartmentsalary
1AliceSales70000.00
2BobSales80000.00
3CharlieHR60000.00
4DavidHR70000.00
5EveEngineering95000.00
6FrankEngineering95000.00
9AlaEngineering
11GraceMarketing50000.00
12HannahMarketing55000.00
13TomEngineering55000.00

假如我要查出每个部门工资最高的员工(可能会出现多人并列第一的情况),可以使用下面的SQL实现:

   select
	name,
	department,
	rk
from
	(
	select
		name,
		department,
		rank() over(partition by department
	order by
		salary desc) rk
	from
		employees e 
   ) as temp
where
	rk = 1

运行结果为:

namedepartmentrk
EveEngineering1
FrankEngineering1
DavidHR1
HannahMarketing1
BobSales1

2.2、rank()的问题

从上面的结果来看没有问题,确实能查出来每个部门工资最高的员工,其中Engineering部门Eve和Frank工资并列第一高。但是SQL职工的子查询存在问题,只是在这场景中没有外现,不妨单独执行一个子查询:

select
		name,
		department,
		rank() over(partition by department
	order by
		salary desc) rk
	from
		employees e

执行结果为:

namedepartmentrk
EveEngineering1
FrankEngineering1
TomEngineering3
AlaEngineering4
DavidHR1
CharlieHR2
HannahMarketing1
GraceMarketing2
BobSales1
AliceSales2

不难看出Engineering部门的Tom被排在了第三名,这在逻辑上显然是不对的(当然如果需求如此那就使用rank()函数),因为Eve和Frank是并列第一名,Tom显然应该是第二名。

2.3、更符合逻辑的dense_rank()

为了使查询结果更合理一些,可以使用dense_rank()函数进行排名,dense有“密集”之意,这也传递出了使用该方法的排名其排名值中没有间隙——排名值是连续增长的。下面是SQL及执行结果:

select
		name,
		department,
		dense_rank() over(partition by department
	order by
		salary desc) rk
	from
		employees e
namedepartmentrk
EveEngineering1
FrankEngineering1
TomEngineering2
AlaEngineering3
DavidHR1
CharlieHR2
HannahMarketing1
GraceMarketing2
BobSales1
AliceSales2

本次查询,部门Engineering中的Tom员工“更合理”地出现在了第二名中。

2.4、有些数据不需要参与排名

在查询排名的示例中,如果需求再“奇葩”一些,salary为空的员工不参与排名,此时SQL可以调整为如下内容:

select
		name,
		department,
		CASE 
			when salary is null then null
			else dense_rank() over(partition by department order by salary desc)
		END as rk
	from
		employees e

查询结果为:

namedepartmentrk
EveEngineering1
FrankEngineering1
TomEngineering2
AlaEngineering
DavidHR1
CharlieHR2
HannahMarketing1
GraceMarketing2
BobSales1
AliceSales2

使用了case when语法使得salary为空的员工不再参与排名。

2.5、只设置行号的函数——row_number()

与rank()、dense_rank()相关的还有一个函数row_number(),该函数再MySQL8以后的版本中才引入,从它的名字也可以看出其作用——为每一行分配唯一的排名。可以从下面的SQL及查询结果看出其运行效果:

select
		name,
		department,
		CASE 
			when salary is null then null
			else row_number() over(partition by department order by salary desc)
		END as rk
	from
		employees e
namedepartmentrk
EveEngineering1
FrankEngineering2
TomEngineering3
AlaEngineering
DavidHR1
CharlieHR2
HannahMarketing1
GraceMarketing2
BobSales1
AliceSales2

严格来讲,row_number()已经不是设置排名了,它是在设置行号。

3、总结

RANK()函数为结果集中的行分配一个唯一的排名,排名基于指定的排序条件。当出现重复值时,RANK()会为这些重复的行分配相同的排名,并在后续排名中跳过相应的名次。例如,如果有两个并列第一的项,下一项将被标记为第三。RANK()与ROW_NUMBER()和DENSE_RANK()的区别在于,ROW_NUMBER()为每一行分配唯一的排名,而DENSE_RANK()则不会跳过排名。通过正确使用ORDER BY和PARTITION BY,可以灵活地对数据进行排名,满足不同的业务需求。
我是欧阳方超,把事情做好了自然就有兴趣了,如果你喜欢我的文章,欢迎点赞、转发、评论加关注。我们下次见。

MySQL 8.0中,可以使用rank()函数实现排名功能。rank()函数用于计算每行在结果集中的排名。它的基本语法如下: rank() over (order by column_name) 其中,order by子句指定了按照哪个字段进行排名rank()函数会根据指定的字段对结果集进行排序,并为每行分配一个排名值。排名值越小,表示排名越高。 举个例子,假设我们有一个名为student的表,其中包含sid、sname和sage三个字段。要计算每个学生的排名,可以使用以下查询语句: SELECT sid, sname, sage, rank() over (order by sage desc) as rank FROM student 这个查询会按照sage字段降序排列学生的成绩,并为每个学生分配一个排名值。排名值越小,表示成绩越高。 需要注意的是,在使用rank()函数时,需要使用over子句指定窗口。在这个例子中,我们没有指定partition by子句,因此整个结果集被视为一个窗口。 总结起来,MySQL 8.0中的rank()函数可以用来计算结果集中每行的排名,通过指定order by子句来确定排名的依据。 #### 引用[.reference_title] - *1* [Mysql基础之rank函数使用](https://blog.csdn.net/m0_60196931/article/details/125214918)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [MYSQL8.0窗口函数](https://blog.csdn.net/cristianoxm/article/details/127144695)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [MySQL8.0窗口函数排名函数rank、dense_rank)的使用](https://blog.csdn.net/weixin_43857827/article/details/113739269)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值