• 569. 员工薪水中位数


    Employee 表包含所有员工。Employee 表有三列:员工Id,公司名和薪水。

    +-----+------------+--------+
    |Id   | Company    | Salary |
    +-----+------------+--------+
    |1    | A          | 2341   |
    |2    | A          | 341    |
    |3    | A          | 15     |
    |4    | A          | 15314  |
    |5    | A          | 451    |
    |6    | A          | 513    |
    |7    | B          | 15     |
    |8    | B          | 13     |
    |9    | B          | 1154   |
    |10   | B          | 1345   |
    |11   | B          | 1221   |
    |12   | B          | 234    |
    |13   | C          | 2345   |
    |14   | C          | 2645   |
    |15   | C          | 2645   |
    |16   | C          | 2652   |
    |17   | C          | 65     |
    +-----+------------+--------+
    

    请编写SQL查询来查找每个公司的薪水中位数。挑战点:你是否可以在不使用任何内置的SQL函数的情况下解决此问题。

    +-----+------------+--------+
    |Id   | Company    | Salary |
    +-----+------------+--------+
    |5    | A          | 451    |
    |6    | A          | 513    |
    |12   | B          | 234    |
    |9    | B          | 1154   |
    |14   | C          | 2645   |
    +-----+------------+--------+


    解法一
    注意到每个公司的薪水升序后,中位数的下标和个数是确定的。

    薪水总数N是偶数时,下标(N-1)/2和N/2位置处为中位数。N是奇数时,下标(N-1)/2为中位数。

    对每个公司group by分组,统计每组个数N,确定中位数区间[beg,beg+cnt],beg从0开始。

    beg = (N-1)/2

    cnt = 0或1,N为偶数时为1,N为奇数时为0。

    结果命名为表A。

    (
    SELECT E.Company,FLOOR((COUNT(*)-1)/2) AS `beg`,if(COUNT(*) % 2=1,0,1) AS `cnt`
    FROM employee AS E
    GROUP BY E.Company
    ) AS A
    COUNT(*)为每组总数,FLOOR向下取整函数。

    计算每个人薪水的升序排名。最小的薪水排第一,第2小的薪水排第二,…..。

    薪水比较方法:

    if (A.salary = B.salary and A.id > B.id or A.salary > B.salary)
    {
    那么A的排名在B的排名后。
    }
    员工表left join员工表,得出同一个公司中,排名在每个人之前的所有人。

    SELECT *
    FROM employee AS E1
    LEFT JOIN employee AS E2
    ON(E1.company = E2.company AND (E1.salary = E2.salary AND E1.Id > E2.Id OR E1.Salary > E2.Salary))
    那么,按E1分组后,统计每组内,E2的个数即为E1的排名,排名从0开始。

    最后结果按薪水升序,命名为表B。

    (
    SELECT E1.Id,E1.Company,E1.Salary, COUNT(E2.Salary) AS `trank`
    FROM employee AS E1
    LEFT JOIN employee AS E2
    ON(E1.company = E2.company AND (E1.salary = E2.salary AND E1.Id > E2.Id OR E1.Salary > E2.Salary))
    GROUP BY E1.Id,E1.Company,E1.Salary
    ORDER BY E1.Company,E1.Salary
    ) AS B
    现在有了中位数区间和每个人的排名。

    连接表A和表B,公司相同,B.trank在[A.beg,A.beg+A.cnt]之间的行,为中位数。

    SELECT B.Id,B.Company,B.Salary
    FROM
    (
    SELECT E.Company,FLOOR((COUNT(*)-1)/2) AS `beg`,if(COUNT(*) % 2=1,0,1) AS `cnt`
    FROM employee AS E
    GROUP BY E.Company
    ) AS A
    JOIN (
    SELECT E1.Id,E1.Company,E1.Salary, COUNT(E2.Salary) AS `trank`
    FROM employee AS E1
    LEFT JOIN employee AS E2
    ON(E1.company = E2.company AND (E1.salary = E2.salary AND E1.Id > E2.Id OR E1.Salary > E2.Salary))
    GROUP BY E1.Id,E1.Company,E1.Salary
    ORDER BY E1.Company,E1.Salary
    ) AS B
    ON (A.company = B.company AND B.trank BETWEEN A.beg AND (A.beg+A.cnt))
    解法二
    思路与解法一相同。换了一种计算公司内部人员排名的方法。

    解法一采用表left join,分组再计数的方法。

    这里,采用用户变量的方法,简化排名的计算。

    定义用户变量:pre_company——上一行的公司字段。trank——当前行的排名,从0开始。

    (SELECT @pre_company:= NULL,@trank:=0) AS T
    计算排名的逻辑:

    if (当前行的company = pre_company){
    trank = trank + 1
    }else{
    trank = 0
    }
    SQL代码,结果命名为表A

    (
    SELECT
    id,
    company,
    salary,
    @trank:=IF(@pre_company=company,@trank+1,0) AS `trank`,
    @pre_company:=company
    FROM employee,(SELECT @pre_company:= NULL,@trank:=0) AS T
    ORDER BY company,salary
    ) AS A
    计算中位数下标的方法如解法一。

    薪水总数N是偶数时,下标(N-1)/2和N/2位置处为中位数。N是奇数时,下标(N-1)/2为中位数。

    对每个公司group by分组,统计每组个数N,确定中位数区间[beg,beg+cnt],beg从0开始。

    beg = (N-1)/2

    cnt = 0或1,N为偶数时为1,N为奇数时为0。

    结果命名为表B。

    (
    SELECT company, FLOOR((COUNT(*)-1)/2) AS `beg`, if(COUNT(*)%2=1,0,1) AS `cnt`
    FROM employee
    GROUP BY company
    ) AS B
    连接表A和表B ,公司相同,B.trank在[A.beg,A.beg+A.cnt]之间的行,为中位数。

    SELECT A.Id,A.Company,A.Salary
    FROM
    (
    SELECT
    id,
    company,
    salary,
    @trank:=IF(@pre_company=company,@trank+1,0) AS `trank`,
    @pre_company:=company
    FROM employee,(SELECT @pre_company:= NULL,@trank:=0) AS T
    ORDER BY company,salary
    ) AS A
    JOIN
    (
    SELECT company, FLOOR((COUNT(*)-1)/2) AS `beg`, if(COUNT(*)%2=1,0,1) AS `cnt`
    FROM employee
    GROUP BY company
    ) AS B
    ON (A.company=B.company AND A.trank BETWEEN B.beg AND B.beg+B.cnt)

    作者:jason-2
    链接:https://leetcode-cn.com/problems/median-employee-salary/solution/liang-chong-jie-fa-by-jason-2-2/
    来源:力扣(LeetCode)
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  • 相关阅读:
    乱码问题
    play之路由 routes
    delphi之http通讯
    delphi之socket通讯
    Delphi之ComboBox
    delphi 常用函数
    字节
    EXCEPT
    V_REPORT_AOC_FUEL]
    相同表结构不同记录
  • 原文地址:https://www.cnblogs.com/leeeee/p/11901988.html
Copyright © 2020-2023  润新知