这道题写的头疼,还是因为太菜了吧。。。
题目
Employee 表包含所有员工信息,每个员工有其对应的 Id, salary 和 department Id。
+----+-------+--------+--------------+
| Id | Name | Salary | DepartmentId |
+----+-------+--------+--------------+
| 1 | Joe | 70000 | 1 |
| 2 | Henry | 80000 | 2 |
| 3 | Sam | 60000 | 2 |
| 4 | Max | 90000 | 1 |
+----+-------+--------+--------------+
Department 表包含公司所有部门的信息。
+----+----------+
| Id | Name |
+----+----------+
| 1 | IT |
| 2 | Sales |
+----+----------+
编写一个 SQL 查询,找出每个部门工资最高的员工。例如,根据上述给定的表格,Max 在 IT 部门有最高工资,Henry 在 Sales 部门有最高工资。
+------------+----------+--------+
| Department | Employee | Salary |
+------------+----------+--------+
| IT | Max | 90000 |
| Sales | Henry | 80000 |
+------------+----------+--------+
审题
如果只要每个部门的最高工资这就是一个特别特别简单的题目 分组+连接即可
但这里还要涉及到最高工资对应的姓名,如果分组取Name其实取到的是每个分组的第一个姓名 不一定是最高工资对应的姓名。 这里可以对各个部门的最高工资和Employee表进行连接得到
产生数据
CREATE TABLE employee2 (
Id INT,
NAME CHAR(10),
Salary INT,
DepartmentId CHAR(2)
) ;
CREATE TABLE Department (Id INT, NAME CHAR(10)) ;
INSERT INTO employee2
VALUE(1, 'Joe', 70000, '1'),
(2, 'Hery', 80000, '2'),
(3, 'Sam', 60000, '2'),
(4, 'Max', 90000, '1');
INSERT INTO Department VALUE(1, 'IT'),(2, 'Sales');
自己的解答
开始绝望
①、选出每个DepartmentId的最高工资
SELECT DepartmentId, MAX(Salary)
FROM Employee2
GROUP BY DepartmentId;
②、①得到的结果与Department表连接得到部门名
SELECT tmp.salary, dep.`Name`
FROM (SELECT DepartmentId, MAX(Salary) AS salary
FROM Employee2
GROUP BY DepartmentId) AS tmp
JOIN Department AS dep
ON tmp.DepartmentId = dep.`Id`;
③、②的结果和Employee表连接 得到最高工资对应的姓名
SELECT tmp2.name AS Department, e.`NAME` AS Employee, tmp2.salary AS salary
FROM employee2 AS e
JOIN (SELECT tmp.salary, dep.`Name`
FROM (SELECT DepartmentId, MAX(Salary) AS salary
FROM Employee2
GROUP BY DepartmentId) AS tmp
LEFT JOIN Department AS dep
ON tmp.DepartmentId = dep.`Id`) tmp2
ON e.`Salary` = tmp2.salary;
执行代码
挺好的呀, 然而。。。
这里有一个坑,是Department如果为空 应该返回空表。
这个可以判断空表
LENGTH(TRIM(Id))>0
FROM Department
但是不知道怎么加进去。。。
为了解决这个问题尝试先用连接再分组
想法:如果用左连接 department表为主表当他为空的时候也就可以解决了
然而。。。
SELECT tmp.Department, e2.`NAME` AS Employee, tmp.salary
FROM (SELECT MAX(e.`Salary`) AS Salary, dep.`Name` AS Department
FROM Department dep
LEFT JOIN employee2 e
ON e.`DepartmentId` = dep.`Id`
GROUP BY dep.`Name`) tmp
LEFT JOIN employee2 e2
ON tmp.Salary = e2.`Salary`
结果:
如果Employee表为空,就又gg了。。。
绝望
挣扎了一会儿。。放弃了
正确的解答
1.in的无情操作
①、选出每个DepartmentId的最高工资
SELECT DepartmentId, MAX(Salary)
FROM Employee2
GROUP BY DepartmentId;
注意:有可能有多个员工同时拥有最高工资,所以最好在这个查询中不包含雇员名字的信息。
而且分组取name,其实取得是每个分组的第一个name
②、in的无情操作
将两表连接
SELECT *
FROM employee2 e
JOIN department dep
ON e.`DepartmentId` = dep.`Id`
之前接触过的in操作都是单个的,两个的也是可以的
③将(dep.Id
, e.Salary
)在表一种的结果取出
SELECT *
FROM employee2 e
JOIN department dep
ON e.`DepartmentId` = dep.`Id`
WHERE (dep.`Id`, e.`Salary`) IN (SELECT DepartmentId, MAX(Salary)
FROM Employee2
GROUP BY DepartmentId);
再把对应的列名取出取别名即可
SELECT dep.`Name` AS Department, e.`NAME` AS Employee, e.`Salary`
FROM employee e
JOIN department dep
ON e.`DepartmentId` = dep.`Id`
WHERE (e.`DepartmentId`, e.`Salary`) IN (SELECT DepartmentId, MAX(Salary)
FROM Employee
GROUP BY DepartmentId);
终于通过了。。。
思考:这里如果出现表为空的情况可以用第二部中的内连接来避免, 如果一个表为空,则内连接的结果为空。
但如果用左连接呢
使用left join 要注意 部门名字 不能是NULL 。所以,如果使用Left join在官方解答后面加上一句判断 AND Department.name is not null...
SELECT
Department.name AS 'Department',
Employee.name AS 'Employee',
Salary
FROM
Employee
LEFT JOIN
Department ON Employee.DepartmentId = Department.Id
WHERE
(Employee.DepartmentId , Salary) IN
( SELECT
DepartmentId, MAX(Salary)
FROM
Employee
GROUP BY DepartmentId
)
AND Department.name is not null
;
后期补充
其实我自己的解答虽然结果是对的 但存在很大问题
有可能有多个员工同时拥有最高工资 如果IT部门也有一个工资为8000的员工 只靠salary连接就会出现很大的问题,其实这里还应该将②中的部门id保留 靠salary和部门id一起连接 下面试一试
SELECT tmp2.name AS Department, e.`NAME` AS Employee, tmp2.salary AS salary
FROM employee AS e
JOIN (SELECT dep.`Id`, tmp.salary, dep.`Name`
FROM (SELECT DepartmentId, MAX(Salary) AS salary
FROM Employee
GROUP BY DepartmentId) AS tmp
JOIN Department AS dep
ON tmp.DepartmentId = dep.`Id`) tmp2
ON e.`Salary` = tmp2.salary AND e.`DepartmentId` = tmp2.Id;
通过咯。。。
总结
前面写的有些乱,在这里总结一下吧
方法一、
先找出每个部门的最高薪水。
连接员工表和部门表,group by对部门分组,再求每组的最高薪水。用子查询得出临时表tmp(id,name,m)。
SELECT dep.id, dep.name,MAX(E.salary) AS m
FROM Department AS dep
JOIN Employee2 AS E
ON (dep.id = E.departmentid)
GROUP BY dep.id,dep.name
再次,连接员工表和临时表tmp,条件是部门id相同且薪水与每个部门最高薪水相同。与上面的组合起来得出算法:
select tmp.name as `Department`,E2.name as `Employee`,E2.Salary
from Employee as E2
join (
SELECT dep.id, dep.name,MAX(E.salary) AS m
FROM Department AS dep
JOIN Employee2 AS E
ON (dep.id = E.departmentid)
GROUP BY dep.id,dep.name
) as tmp
on (E2.departmentid = tmp.id and tmp.m = E2.salary);
可行
另外员工表中有部门id和薪水,但没有部门name。可先求部门的最高薪水,形成临时表,再连接员工表(需要部门Id和Salary两个条件)和部门表【两者的顺序可以调换】,取出部门name和员工name。
select D.name as `Department`,E.name as `Employee`,E.Salary
from Employee as E
join (
select E1.departmentid,max(E1.salary) as m
from Employee as E1
group by E1.departmentid
) as F
on (E.departmentid = F.departmentid and F.m = E.salary)
join Department as D
on (F.departmentid = D.id)
方法二、
对每一个员工,找出员工所在部门的最高薪水。此处的查找过程,用子查询实现。如果员工的薪水等于部门的最高薪水就是结果。
select D.name as `Department`,E.name as `Employee`,E.Salary
from Employee as E
join Department as D
on (E.departmentid = D.id)
where E.salary = (
select max(E1.salary)
from Employee as E1
where E1.departmentid = E.departmentid
)
方法三、
当然二元组(部门id,部门最高薪水)作为整体t,结合in判断t是否在已存在的组合中。
select D.name as `Department`,E.name as `Employee`,E.Salary
from Employee as E
join Department as D
on (E.departmentid = D.id)
where (E.departmentid,E.salary) in (
select E1.departmentid,max(E1.salary)
from Employee as E1
where E1.departmentid
group by E1.departmentid
)