leetcode185 部門工資前三高的全部員工 Department Top Three Salaries

Employee 表包含全部員工信息,每一個員工有對應的 Id,此外還有一列部門 Idhtml

 建立表和數據:mysql

Create table If Not Exists Employee (Idint, Name varchar(255), Salary int, DepartmentId int);
Create table If Not Exists Department (Idint, Name varchar(255));
Truncate table Employee;
insert into Employee (Id, Name, Salary,DepartmentId) values ('1', 'Joe', '70000', '1');
insert into Employee (Id, Name, Salary,DepartmentId) values ('2', 'Henry', '80000', '2');
insert into Employee (Id, Name, Salary,DepartmentId) values ('3', 'Sam', '60000', '2');
insert into Employee (Id, Name, Salary,DepartmentId) values ('4', 'Max', '90000', '1');
insert into Employee (Id, Name, Salary,DepartmentId) values ('5', 'Janet', '69000', '1');
insert into Employee (Id, Name, Salary,DepartmentId) values ('6', 'Randy', '85000', '1');
Truncate table Department;
insert into Department (Id, Name) values('1', 'IT');
insert into Department (Id, Name) values('2', 'Sales');

解法:sql

1.判斷每一個人A是否是在這三批人中的一個。找出同一部門種比A薪水高的薪水種數N。用子查詢完成。若是N<3,那麼A屬於這三批人。spa

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 (
    select count(distinct E1.salary)
    from Employee as E1
    where E1.departmentid = E.departmentid and E1.salary > E.salary
) <3

2.先找出每一個部門薪水第三高的薪水A。每一個人的薪水只要大於等於A,他確定在這三批人中翻譯

SELECT *
FROM Employee e1
LEFT JOIN Employee e2 ON(e1.DepartmentId=e2.DepartmentId AND e1.Salary>e2.Salary)
LEFT JOIN Employee e3 ON(e2.DepartmentId=e3.DepartmentId AND e2.Salary>e3.Salary);
+------+-------+--------+--------------+------+-------+--------+--------------+------+-------+--------+--------------+
| Id   | Name  | Salary | DepartmentId | Id   | Name  | Salary | DepartmentId | Id   | Name  | Salary | DepartmentId |
+------+-------+--------+--------------+------+-------+--------+--------------+------+-------+--------+--------------+
|    4 | Max   |  90000 |            1 |    1 | Joe   |  85000 |            1 |    5 | Janet |  69000 |            1 |
|    4 | Max   |  90000 |            1 |    6 | Randy |  85000 |            1 |    5 | Janet |  69000 |            1 |
|    1 | Joe   |  85000 |            1 |    7 | Will  |  70000 |            1 |    5 | Janet |  69000 |            1 |
|    4 | Max   |  90000 |            1 |    7 | Will  |  70000 |            1 |    5 | Janet |  69000 |            1 |
|    6 | Randy |  85000 |            1 |    7 | Will  |  70000 |            1 |    5 | Janet |  69000 |            1 |
|    4 | Max   |  90000 |            1 |    1 | Joe   |  85000 |            1 |    7 | Will  |  70000 |            1 |
|    4 | Max   |  90000 |            1 |    6 | Randy |  85000 |            1 |    7 | Will  |  70000 |            1 |
|    2 | Henry |  80000 |            2 |    3 | Sam   |  60000 |            2 | NULL | NULL  |   NULL |         NULL |
|    1 | Joe   |  85000 |            1 |    5 | Janet |  69000 |            1 | NULL | NULL  |   NULL |         NULL |
|    4 | Max   |  90000 |            1 |    5 | Janet |  69000 |            1 | NULL | NULL  |   NULL |         NULL |
|    6 | Randy |  85000 |            1 |    5 | Janet |  69000 |            1 | NULL | NULL  |   NULL |         NULL |
|    7 | Will  |  70000 |            1 |    5 | Janet |  69000 |            1 | NULL | NULL  |   NULL |         NULL |
|    3 | Sam   |  60000 |            2 | NULL | NULL  |   NULL |         NULL | NULL | NULL  |   NULL |         NULL |
|    5 | Janet |  69000 |            1 | NULL | NULL  |   NULL |         NULL | NULL | NULL  |   NULL |         NULL |
+------+-------+--------+--------------+------+-------+--------+--------------+------+-------+--------+--------------+

從結果中發現,求第三高的薪水,只能在e3.Salary上求max。且要處理 e3.Salary 爲null的狀況。若是在 e1.Salary 上求max,獲得的必定是每一個部門的最高薪水。所以left join 左邊的表的全部元組必然在結果中。3d

CASE WHEN END子句,對null字段進行處理。code

在這裏當值爲NULL時,將其替換爲0。htm

SELECT e1.DepartmentId, 
CASE 
    WHEN MAX(e3.salary) IS NULL THEN 0 
    ELSE MAX(e3.salary) 
END AS max_salary
FROM Employee e1
LEFT JOIN Employee e2 ON(e1.DepartmentId=e2.DepartmentId AND e1.Salary>e2.Salary)
LEFT JOIN Employee e3 ON(e2.DepartmentId=e3.DepartmentId AND e2.Salary>e3.Salary)
GROUP BY e1.DepartmentId

上面說順序很重要,那麼若是將上面的小於號的順序,改成e1.Salary<e2.Salary<e3.Salary。 再求e1.Salary的max,得出的值必定時每一個部門最高的薪水,而不是第三高的薪水。由於left join最左邊的表的元組必定所有都在!blog

 

將上面的結果與員工表和部門表,再鏈接起來,得出結果。排序

SELECT D.name AS Department,E.name AS Employee,E.Salary
FROM Employee AS E
JOIN Department AS D ON (E.departmentid = D.id)
JOIN (
    SELECT e1.DepartmentId, CASE WHEN MAX(e3.salary) IS NULL THEN 0 ELSE MAX(e3.salary) END AS m
    FROM Employee e1
    LEFT JOIN Employee e2 ON(e1.DepartmentId=e2.DepartmentId AND e1.Salary>e2.Salary)
    LEFT JOIN Employee e3 ON(e2.DepartmentId=e3.DepartmentId AND e2.Salary>e3.Salary)
    GROUP BY e1.DepartmentId
) AS F ON (E.departmentid = F.departmentid AND E.salary >= F.m)

3.延續解法二的思路。 應用用戶變量,求每一個部門第三高薪水。

 

定義三個用戶變量:@pre_salary——上一行的薪水;@pre_deptid——上一行的部門id;@salary_cnt——第幾種薪水。

SELECT @pre_salary:= NULL, @pre_deptid:= NULL, @salary_cnt:=0;

其初始值以下,構成了一個表。

+--------------------+--------------------+----------------+
| @pre_salary:= NULL | @pre_deptid:= NULL | @salary_cnt:=0 |
+--------------------+--------------------+----------------+
| NULL               | NULL               |              0 |
+--------------------+--------------------+----------------+

將這樣的表命名爲A。

(
    SELECT @pre_salary:= NULL, @pre_deptid:= NULL, @salary_cnt:=0
) 
AS A

先看將員工表與表A叉積。

select *
FROM Employee t, 
(
    SELECT @pre_salary:= NULL, @pre_deptid:= NULL, @salary_cnt:=0
) 
AS A

結果爲

+------+-------+--------+--------------+--------------------+--------------------+----------------+
| Id   | Name  | Salary | DepartmentId | @pre_salary:= NULL | @pre_deptid:= NULL | @salary_cnt:=0 |
+------+-------+--------+--------------+--------------------+--------------------+----------------+
|    1 | Joe   |  85000 |            1 | NULL               | NULL               |              0 |
|    2 | Henry |  80000 |            2 | NULL               | NULL               |              0 |
|    3 | Sam   |  60000 |            2 | NULL               | NULL               |              0 |
|    4 | Max   |  90000 |            1 | NULL               | NULL               |              0 |
|    5 | Janet |  69000 |            1 | NULL               | NULL               |              0 |
|    6 | Randy |  85000 |            1 | NULL               | NULL               |              0 |
|    7 | Will  |  70000 |            1 | NULL               | NULL               |              0 |
+------+-------+--------+--------------+--------------------+--------------------+----------------+

但這樣的結果,對找每一個部門的不一樣薪水沒有幫助。要對結果按部門id遞增排序,再按薪水降序。

select *
FROM Employee t, 
(
    SELECT @pre_salary:= NULL, @pre_deptid:= NULL, @salary_cnt:=0
) 
AS A
ORDER BY t.DepartmentId, t.Salary DESC

排序後

+------+-------+--------+--------------+--------------------+--------------------+----------------+
| Id   | Name  | Salary | DepartmentId | @pre_salary:= NULL | @pre_deptid:= NULL | @salary_cnt:=0 |
+------+-------+--------+--------------+--------------------+--------------------+----------------+
|    4 | Max   |  90000 |            1 | NULL               | NULL               |              0 |
|    1 | Joe   |  85000 |            1 | NULL               | NULL               |              0 |
|    6 | Randy |  85000 |            1 | NULL               | NULL               |              0 |
|    7 | Will  |  70000 |            1 | NULL               | NULL               |              0 |
|    5 | Janet |  69000 |            1 | NULL               | NULL               |              0 |
|    2 | Henry |  80000 |            2 | NULL               | NULL               |              0 |
|    3 | Sam   |  60000 |            2 | NULL               | NULL               |              0 |
+------+-------+--------+--------------+--------------------+--------------------+----------------+

從這個結果中找出,每一個部門的不一樣薪水個數。

 

對結果中的每行,執行下述邏輯,計數。

if (當前行的salary = @pre_salary and 當前行的departmentid = @pre_deptid)
{
    //相同的薪水
    @salary_cnt 不變
}
else if(當前行的departmentid = @pre_deptid)
{
    //不一樣的薪水
    @salary_cnt = @salary_cnt + 1
}
else
{
    //不一樣的部門,計數從新從1開始
    @salary_cnt = 1
}
//更新pre_salary和pre_deptid
@pre_salary = 當前行的salary
@pre_deptid = 當前行的departmentid

將此邏輯翻譯爲SQL代碼,結果命令爲表B

(
    SELECT
    @salary_cnt:= IF(t.Salary = @pre_salary AND t.DepartmentId = @pre_deptid, 
                @salary_cnt, 
            IF(t.DepartmentId = @pre_deptid, 
                             @salary_cnt + 1, 
                             1)
        ) 
        AS `cnt`
    ,@pre_salary := t.Salary AS `salary`
    ,@pre_deptid := t.DepartmentId AS `deptid`
    FROM Employee t, 
    (
        SELECT @pre_salary:= NULL, @pre_deptid:= NULL, @salary_cnt:=0
    ) 
    AS A
    ORDER BY t.DepartmentId, t.Salary DESC
) 
AS B

結果爲:

+------+--------+--------+
| cnt  | salary | deptid |
+------+--------+--------+
|    1 |  90000 |      1 |
|    2 |  85000 |      1 |
|    2 |  85000 |      1 |
|    3 |  70000 |      1 |
|    4 |  69000 |      1 |
|    1 |  80000 |      2 |
|    2 |  60000 |      2 |
+------+--------+--------+

從表B中,選出每一個部門中前三種薪水,取最小的薪水。結果命名爲C。

(
SELECT B.deptid,MIN(B.salary) AS `salary`
FROM 
    (
        SELECT
        @salary_cnt:= IF(t.Salary = @pre_salary AND t.DepartmentId = @pre_deptid, 
            @salary_cnt, 
                IF(t.DepartmentId = @pre_deptid, @salary_cnt + 1, 1)
            ) 
            AS `cnt`
        ,@pre_salary := t.Salary AS `salary`
        ,@pre_deptid := t.DepartmentId AS `deptid`
        FROM Employee t, 
        (
            SELECT @pre_salary:= NULL, @pre_deptid:= NULL, @salary_cnt:=0
        ) 
        AS A
        ORDER BY t.DepartmentId, t.Salary DESC
    ) 
    AS B
WHERE B.cnt < 4
GROUP BY B.deptid
) AS C

結果爲:

+--------+--------+
| deptid | salary |
+--------+--------+
|      1 |  70000 |
|      2 |  60000 |
+--------+--------+

再將表C與員工表和部門錶鏈接,全部薪水大於等於C.salary都取出來。

SELECT D.name AS `Department`,E.name AS `Employee`,E.Salary
FROM Employee AS E
JOIN Department AS D ON (E.departmentid = D.id)
JOIN 
    (
    SELECT B.deptid,MIN(B.salary) AS `salary`
    FROM 
        (
            SELECT
            @salary_cnt:= IF(t.Salary = @pre_salary AND t.DepartmentId = @pre_deptid, 
                @salary_cnt, 
                    IF(t.DepartmentId = @pre_deptid, @salary_cnt + 1, 1)
                ) 
                AS `cnt`
            ,@pre_salary := t.Salary AS `salary`
            ,@pre_deptid := t.DepartmentId AS `deptid`
            FROM Employee t, 
            (
                SELECT @pre_salary:= NULL, @pre_deptid:= NULL, @salary_cnt:=0
            ) 
            AS A
            ORDER BY t.DepartmentId, t.Salary DESC
        ) 
        AS B
    WHERE B.cnt < 4
    GROUP BY B.deptid
    ) AS C
    ON (D.id = C.deptid AND E.salary >= C.salary)

4.計算每一個員工的薪水,在其所在部門的薪水中的排名。最後取出每一個部門中排名前三的員工。內在的邏輯與解法三異曲同工,可是具體的方法不一樣。

先取出每一個部門不一樣薪水,並按部門id升序,薪水降序。結果命名爲表A。

(
SELECT DepartmentId,Salary
FROM Employee
GROUP BY DepartmentId,Salary
ORDER BY DepartmentId,Salary DESC
)
AS A

結果爲:

+--------------+--------+
| DepartmentId | Salary |
+--------------+--------+
|            1 |  90000 |
|            1 |  85000 |
|            1 |  70000 |
|            1 |  69000 |
|            2 |  80000 |
|            2 |  60000 |
+--------------+--------+

在表A上求出每一個薪水的排名。也是藉助於用戶變量:@pre_deptid——上一行的部門id,@rank——此行的排名。

(SELECT @pre_deptid:=null, @rank:=0) AS B

叉積A和B,執行下面的邏輯計算@rank。

if (@pre_deptid = 當前行的departmentid)
{
    //同一部門
    @rank = @rank + 1
}
else
{
    //不一樣部門。@rank重置爲1
    @rank=1
}

邏輯轉換爲:

CASE 
    WHEN @pre_deptid = DepartmentId THEN @rank:= @rank + 1
    WHEN @pre_deptid := DepartmentId THEN @rank:= 1
END AS `rank`

薪水排名邏輯,結果命名爲

(
SELECT A.DepartmentId, A.Salary,
       CASE 
            WHEN @pre_deptid = DepartmentId THEN @rank:= @rank + 1
            WHEN @pre_deptid := DepartmentId THEN @rank:= 1
       END AS `rank`
FROM (SELECT @pre_deptid:=null, @rank:=0) 
       AS B,
     (
         SELECT DepartmentId,Salary
         FROM Employee
         GROUP BY DepartmentId,Salary
         ORDER BY DepartmentId,Salary DESC
     ) 
         AS A
)
AS C

結果爲

+--------------+--------+------+
| DepartmentId | Salary | rank |
+--------------+--------+------+
|            1 |  90000 |    1 |
|            1 |  85000 |    2 |
|            1 |  70000 |    3 |
|            1 |  69000 |    4 |
|            2 |  80000 |    1 |
|            2 |  60000 |    2 |
+--------------+--------+------+

再將表C與員工表和部門錶鏈接,取出排名小於等於3的員工。

SELECT D.Name as Department, E.NAME  AS Employee, E.Salary
FROM (
        SELECT A.DepartmentId, A.Salary,
               CASE 
                    WHEN @pre_deptid = DepartmentId THEN @rank:= @rank + 1
                    WHEN @pre_deptid := DepartmentId THEN @rank:= 1
               END AS `rank`
        FROM (SELECT @pre_deptid:=null, @rank:=0) 
               AS B,
             (
                 SELECT DepartmentId,Salary
                 FROM Employee
                 GROUP BY DepartmentId,Salary
                 ORDER BY DepartmentId,Salary DESC
             ) 
                 AS A
       )
       AS C
INNER JOIN Department AS D ON C.DepartmentId = D.Id
INNER JOIN Employee AS E ON C.DepartmentId = E.DepartmentId AND C.Salary = E.Salary AND C.rank <= 3

 

 

從結果中發現,求第三高的薪水,只能在e3.Salary上求max。且要處理 e3.Salary 爲null的狀況。若是在 e1.Salary 上求max,獲得的必定是每一個部門的最高薪水。所以left join 左邊的表的全部元組必然在結果中。

CASE WHEN END子句,對null字段進行處理。

在這裏當值爲NULL時,將其替換爲0。

相關文章
相關標籤/搜索