部门前三名薪水

时间:2017-01-25 22:29:18

标签: mysql

来自leetcode的原始问题:

The Employee table holds all employees. Every employee has an Id, and there is also 
a column for the department Id.

+----+-------+--------+--------------+
| Id | Name  | Salary | DepartmentId |
+----+-------+--------+--------------+
| 1  | Joe   | 70000  | 1            |
| 2  | Henry | 80000  | 2            |
| 3  | Sam   | 60000  | 2            |
| 4  | Max   | 90000  | 1            |
| 5  | Janet | 69000  | 1            |
| 6  | Randy | 85000  | 1            |
+----+-------+--------+--------------+
The Department table holds all departments of the company.

+----+----------+
| Id | Name     |
+----+----------+
| 1  | IT       |
| 2  | Sales    |
+----+----------+
Write a SQL query to find employees who earn the top three salaries in each of the department. For the above tables, your SQL query should return the following rows.

+------------+----------+--------+
| Department | Employee | Salary |
+------------+----------+--------+
| IT         | Max      | 90000  |
| IT         | Randy    | 85000  |
| IT         | Joe      | 70000  |
| Sales      | Henry    | 80000  |
| Sales      | Sam      | 60000  |
+------------+----------+--------+

我在线学习了一个解决方案,通过自我加入然后加入部门表。

以下是代码:

SELECT d.Name AS Department, e.Name AS Employee, e.Salary
FROM (SELECT e1.Name, e1.Salary, e1.DepartmentId FROM Employee e1 
JOIN Employee e2 WHERE e1.DepartmentId = e2.DepartmentId AND e1.Salary <= e2.Salary 
GROUP BY e1.DepartmentId HAVING COUNT(DISTINCT(e2.Salary)) <= 3) e
JOIN Department d
ON e.DepartmentId = d.Id
ORDER BY d.Name, e.Salary DESC

我没有得到正确的结果:

Output:
{"headers": ["Department", "Employee", "Salary"], "values": [["IT", "Joe", 60000]]}
Expected:
{"headers": ["Department", "Employee", "Salary"], "values": [["IT", "Joe", 60000], 
["IT", "Max", 60000]]}

但是,当GROUP BY更改为e1.Id时,它会起作用。

我不知道为什么会这样。

4 个答案:

答案 0 :(得分:1)

了解复杂查询的快速指南

我建议你不要直接告诉你答案 练习。这样你不仅可以理解这个查询,还可以理解 一种了解未来复杂查询的方法:

第1步:缩进查询

您应该缩进查询,使其看起来如下所示 查询的结构更加明显:

SELECT 
    d.Name AS Department
    , e.Name AS Employee
    , e.Salary
FROM 
    (
        SELECT                                      -- L1
            e1.Name
            , e1.Salary
            , e1.DepartmentId 
        FROM 
            Employee e1 
            JOIN Employee e2 
        WHERE 
            e1.DepartmentId = e2.DepartmentId 
            AND e1.Salary <= e2.Salary              -- L2
        -- GROUP BY e1.DepartmentId -- not working  
        GROUP BY e1.id              -- working      -- L3
        HAVING COUNT(DISTINCT(e2.Salary)) <= 3      -- L4
    ) e
    JOIN Department d
        ON e.DepartmentId = d.Id
ORDER BY d.Name, e.Salary DESC

第2步:尝试从最里面的子查询中了解并向外扩展您的理解

在这种情况下,最里面的子查询是e(从L1L4)。但 甚至这个子查询也有多个子句,它们构建了一个子句 另一个。所以我首先运行这个非常简单的查询(它是一个自连接, 正如你所说的那样,从L1L2,你看到了什么?

        SELECT                                      -- L1
            e1.Name
            , e1.Salary
            , e1.DepartmentId 
        FROM 
            Employee e1 
            JOIN Employee e2 
        WHERE 
            e1.DepartmentId = e2.DepartmentId 
            AND e1.Salary <= e2.Salary              -- L2

现在当你看到某些内容(与自我加入的效果相关)时,请尝试 再添加一个子句。在这种情况下尝试L3(和非工作 更换)

        -- GROUP BY e1.DepartmentId -- not working  
        GROUP BY e1.id              -- working      -- L3

GROUP BY用于聚合,它将是 奇怪的是在查询中看不到任何聚合函数,然后你 可能会注意到有一个聚合函数(COUNT(DISTINCT(e2.Salary))) 要在下一步中添加(对于HAVING子句),您可以 将此函数添加到其中一个选择表达式中以查看内容 它得到的价值。所以,现在尝试这个查询(即添加了L3和L5):

        SELECT                                      -- L1
            e1.Name
            , e1.Salary
            , e1.DepartmentId 
            , COUNT(DISTINCT(e2.Salary))            -- L5
        FROM 
            Employee e1 
            JOIN Employee e2 
        WHERE 
            e1.DepartmentId = e2.DepartmentId 
            AND e1.Salary <= e2.Salary              -- L2
        GROUP BY e1.id              -- working      -- L3

你看到了什么?你看到了GROUP BY的效果吗?

现在尝试添加一个子句 - L4 - 并查看您是否理解 它做了什么

        HAVING COUNT(DISTINCT(e2.Salary)) <= 3      -- L4

到现在为止,您应该了解e做了什么,现在你把它放在一边 一起查询(从这个答案的最顶部开始的列表),和 你现在明白为什么查询有用吗?

祝你好运!

答案 1 :(得分:1)

我使用的解决方案类似于ROW_NUMBER而不是其他DBMS使用的PARTITION。

select @rn := case when @DepartmentId = DepartmentId then @rn + 1 else @rn := 1 end as rn,
       Id, Name, Salary, DName,
       @DepartmentId := DepartmentId as DepartmentId
from 
     (select @rn := 0 as rn) r,
     (select @DepartmentId := e.DepartmentId as DepartmentId, e.Name, e.Salary, e.Id, d.Name as DName
      from employ1 e
      inner join depart1  d on d.Id = e.DepartmentId
      order by e.DepartmentId, e.Salary desc) es;

这句话返回下一个值:

+----+----+-------+--------+-------+--------------+
| rn | Id |  Name | Salary | DName | DepartmentId |
+----+----+-------+--------+-------+--------------+
|  1 |  4 |  Max  |  90000 |   IT  |       1      |
+----+----+-------+--------+-------+--------------+
|  2 |  6 | Randy |  85000 |   IT  |       1      |
+----+----+-------+--------+-------+--------------+
|  3 |  1 |  Joe  |  70000 |   IT  |       1      |
+----+----+-------+--------+-------+--------------+
|  4 |  5 | Janet |  69000 |   IT  |       1      |
+----+----+-------+--------+-------+--------------+
|  1 |  2 | Henry |  80000 | Sales |       2      |
+----+----+-------+--------+-------+--------------+
|  2 |  3 |  Sam  |  60000 | Sales |       2      |
+----+----+-------+--------+-------+--------------+

正如您所看到的,有一个列可以为每个部门重置它的值。

然后只需获取rn&lt; = 3的行来获取前3个工资。

select DName, Name, Salary
from (
        select @rn := case when @DepartmentId = DepartmentId then @rn + 1 else @rn := 1 end as rn,
               Id, Name, Salary, DName,
               @DepartmentId := DepartmentId as DepartmentId
        from 
             (select @rn := 0 as rn) r,
             (select @DepartmentId := e.DepartmentId as DepartmentId, e.Name, e.Salary, e.Id, d.Name as DName
              from employ1 e
              inner join depart1  d on d.Id = e.DepartmentId
              order by e.DepartmentId, e.Salary desc) es
    ) f
where rn <= 3;

这是最终结果:

+-------+-------+--------+
| DName |  Name | Salary |
+-------+-------+--------+
|   IT  |  Max  |  90000 |
+-------+-------+--------+
|   IT  | Randy |  85000 |
+-------+-------+--------+
|   IT  |  Joe  |  70000 |
+-------+-------+--------+
| Sales | Henry |  80000 |
+-------+-------+--------+
| Sales |  Sam  |  60000 |
+-------+-------+--------+

可在此处查看:http://rextester.com/MDK2215

答案 2 :(得分:0)

    SELECT    
        d.name  as "Department"                     
        ,e1.Name as "Employee"
        , e1.Salary as "Salary"

    FROM 
        Employee e1 
        JOIN Employee e2  JOIN Department d

    WHERE 
        e1.DepartmentId = e2.DepartmentId 
        AND e1.Salary <= e2.Salary  AND d.id = e2.DepartmentId


    GROUP BY d.name,e1.id
    HAVING COUNT(DISTINCT(e2.Salary)) <= 3
    ORDER BY d.name , salary DESC                

注意**

Leetcode答案未按承诺输出行。

这是匹配的“预期”答案:

{“标题”:[“部门”,“雇员”,“工资”],“值”:[[“ IT”,“最大”,90000],[“ IT”,“乔”,70000] ,[“ Sales”,“ Henry”,80000],[“ Sales”,“ Sam”,60000]]}

答案 3 :(得分:0)

使用的基本逻辑-

  1. 基于DepartmentId加入两个表,因为那是两个表中的共同列。
  2. 使用DENSE_RANK()函数PARTITION BY DepartmentIdORDER BY Salary DESC为薪水分配等级,因为我们想要每个部门的前三名薪水。
  3. 获取密集等级值为<= 3的详细信息。

DENSE_RANK()函数将为特定部门中具有相同价值的薪水分配相同的等级。

SELECT details.Department "Department", details.Employee "Employee",
details.Salary "Salary", details.dense_rank AS DENSE_RANK FROM
(SELECT e.Name AS Employee, e.Salary AS Salary, e.DepartmentId, d.Name AS Department, DENSE_RANK() OVER (PARTITION BY e.DepartmentId ORDER BY e.Salary DESC) AS dense_rank
FROM Employee e, Department d
WHERE e.DepartmentId = d.Id) details
WHERE dense_rank <=3;

根据上述查询输出:

["Department", "Employee", "Salary", "DENSE_RANK"]
["IT",          "Max",      90000,     1]
["IT",          "Joe",      85000,     2] --same rank because same salary
["IT",          "Randy",    85000,     2] --same rank because same salary
["IT",          "Will",     70000,     3]
["Sales",       "Henry",    80000,     1]
["Sales",       "Sam",      60000,     2]

https://github.com/hetalrachh/leetcode/blob/master/Hard/DepartmentTop3Salaries.sql检查实际查询