表: Employee

Column NameType
idint
namevarchar
salaryint
departmentIdint

id 是该表的主键列(具有唯一值的列)。
departmentId 是 Department 表中 ID 的外键(reference 列)。
该表的每一行都表示员工的ID、姓名和工资。它还包含了他们部门的ID。

表: Department

Column NameType
idint
namevarchar

id 是该表的主键列(具有唯一值的列)。
该表的每一行表示部门ID和部门名。

公司的主管们感兴趣的是公司每个部门中谁赚的钱最多。
一个部门的 高收入者 是指一个员工的工资在该部门的 不同 工资中 排名前三 。

编写解决方案,找出每个部门中 收入高的员工 。
以 任意顺序 返回结果表。
返回结果格式如下所示。
示例 1:

输入:

Employee 表:
idnamesalarydepartmentId
1Joe850001
2Henry800002
3Sam600002
4Max900001
5Janet690001
6Randy850001
7Will700001
Department 表:
idname
1IT
2Sales
输出:
DepartmentEmployeeSalary
ITMax90000
ITJoe85000
ITRandy85000
ITWill70000
SalesHenry80000
SalesSam60000

解释:
在IT部门:

  • Max的工资最高
  • 兰迪和乔都赚取第二高的独特的薪水
  • 威尔的薪水是第三高的

在销售部:

  • 亨利的工资最高
  • 山姆的薪水第二高
  • 没有第三高的工资,因为只有两名员工

题解:
啊啊啊啊,花了俩小时才搞出来。。。。
不知道还有没有更简单的。。。我这应该也算暴力的一种吧。
1.给每个部门的人员薪资做个薪资等级排名,并且部门之间薪资相同的薪资等级相同,标记为salaryRank
找每个部门前三的问题就变成了找salaryRank<=3的员工。至于部门名字,直接把Department表当成码表即可。
之后找前n等级的只需要更改1个数字就行,即salaryRank<=n
2.编写每个部门的人员薪资做个薪资等级排名临时表
包括Employee.departmentId,Employee.name,Employee.Salary
还有最重要的需要处理的Employee.salaryRank
在这里使用窗口函数和临时变量创建临时表。
3.首先我们先利用窗口函数的row_number()来个普通版本的各部门的等级排名,即薪资重复的情况不考虑。

select departmentId,salary,name
       ,row_number()  over(partition by departmentId order by salary desc) salaryRank
    from employee

可以发现下图的查询结果,各部门的薪资都有个薪资等级salaryRank,但目前薪资重复时,薪资等级并不是相同的,因此下面我们通过临时变量处理下薪资相同时的情况。
2024-08-11T13:49:35.png
4.需要考虑的情况是,各部门按照薪资排序后,如果当前的salary与上一个salary相同时,薪资等级需要与上一个的薪资等级相同,因此减去当前重复的个数即可。
(即Randy的薪资是第1发生重复的,即实际的薪资等级=row_numbe() - 1,同理第n个重复的就是row_numbe() - n)
加上临时变量@salary(临时的salary)和@ranknum(重复的个数),并且要注意部门发生变化时,@ranknum记得初始化。

select departmentId,salary,name
            , row_number()  over(partition by departmentId order by salary desc) 
            - (case when @departmentId = departmentId then (case when @salary = salary then @ranknum:=@ranknum+1 else @ranknum:=@ranknum end) else @ranknum:=0 end) salaryRank
            ,@salary := salary temp1
            ,@departmentId := departmentId temp2
        from employee,(select @ranknum:=0,@salary:=null,@departmentId:=null) temp

查询如下图所示。
2024-08-11T13:50:51.png

注意如果@ranknum没有初始化,只写了像下面一样的。

select departmentId,salary,name
            , row_number()  over(partition by departmentId order by salary desc) 
            - (case when @salary = salary then @ranknum:=@ranknum+1 else @ranknum:=@ranknum end) salaryRank
            ,@salary := salary temp1
        from employee,(select @ranknum:=0,@salary:=null) temp

我们会发现Henry的薪资等级salaryRank变成了0,这是因为上个分组的@ranknum没有重新初始化为0,上个分组有1个重复薪资,导致之后的分组salaryRank全都偏移了1位。
并且如果@ranknum没有初始化,每个分组的重复数量都会累加到@ranknum中,甚至会导致薪资等级salaryRank变成负数。
因此case when @department_id = department_id then () else @ranknum:=0 end 这一句就是为了初始化而加上的。

2024-08-11T13:51:55.png
5.现在我们有了1张临时表,后面的就简单了吧!
临时表
2024-08-11T13:52:30.png

直接选择salaryRank<=3的即可得到结果。

select d.name Department, t.name Employee,Salary
from 临时表
left join department d
on d.id = t.departmentId
where salaryRank <=3
order by t.departmentId,t.Salary desc

最终我们可以得到代码:

with t as (
select departmentId,salary,name
            , row_number()  over(partition by departmentId order by salary desc) 
            - (case when @departmentId = departmentId then (case when @salary = salary then @ranknum:=@ranknum+1 else @ranknum:=@ranknum end) else @ranknum:=0 end) salaryRank
            ,@salary := salary temp1
            ,@departmentId := departmentId temp2
        from employee,(select @ranknum:=0,@salary:=null,@departmentId:=null) temp
)

select d.name Department, t.name Employee,Salary
from t
left join department d
on d.id = t.departmentId
where salaryRank <=3
order by t.departmentId,t.Salary desc

最终得到结果:
2024-08-11T13:54:16.png

最后修改:2024 年 08 月 25 日
游客大老爷,如果觉得我的文章对你有用,请随意赞赏!