0%

从“各组得分最高的员工”到“各组得分前N高的员工“,以”部门公司前三高的所有员工“为例。

预备知识

首先需要了解“各组得分最高的员工”要如何求解,参考这篇文章:
如何查找各组得分最高的员工?以“部门工资最高的员工”为例。

类比”184.部门工资最高的员工“,可以考虑找出每个分组的工资前三高的值,然后通过子查询或者是连接这个临时表找出各组工资前三高的员工。

但实际上,各组前三高的工资并不能像各组最高的工资一样直接group by就能求出,所以这个方法并没有想象中那么简单。

前N高的含义

所以可以考虑工资前N高的含义,即工资比这些员工高的不到N个。

以下以 leetcode 185.部门工资前三高的所有员工为例。

1. 子查询

可以使用子查询,对于每一条数据在子查询中查找工资更高的,并通过count()统计个数。

由于子查询跟外部查询相关,因此这里的子查询是相关子查询,对于外部的每一条数据都要进行一次子查询,效率较低。

1
2
3
4
5
6
7
select d.Name as Department, e1.name as Employee, e1.Salary
from Employee e1 join Department d on e1.DepartmentId = d.Id
where 3 > (
select count(distinct e2.Salary)
from Employee e2
where e1.DepartmentId = e2.DepartmentId and e1.Salary < e2.Salary
);

2. 自连接

由于上述相关子查询的效率较低,因此可以考虑使用自连接,通过on控制条件,使得连接起来的数据属于同一个部门,并且之前存在工资的大小关系。

这条连接查询语句的效率要比上述使用相关子查询的语句高。

1
2
3
4
select d.Name as Department, e1.name as Employee, e1.Salary
from Employee e1 join Department d on e1.DepartmentId = d.Id join Employee e2 on e1.DepartmentId = e2.DepartmentId and e1.Salary <= e2.Salary
group by e1.Id
having 3 >= count(distinct e2.Salary);

扩展

类似地,也可以使用这种方法找出”所有人中排名前N的员工“(可以直接order by),”每个组中排名前N的员工“,以及”所有人/每个部门排名第N的员工“(order by … limit 或上述换为等号)等。