• SQL-去除最大值与最小值求均值的问题


    • 背景

    今天有同事问我一道关于数据库SQL的面试题,我刚开始随便给了一个思路,后来思索发现这个思路有漏洞,于是总结下来,仅供参考。

    问题: 薪水表中是员工薪水的基本信息,包括雇员编号,和薪水,查询除去最高、最低薪水后的平均薪水。

    • 一、薪水表信息
    CREATE TABLE `salaries` (
      `emp_no` int NOT NULL,
      `salary` int NOT NULL
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

    测试数据如下:

    复制代码
    INSERT INTO `salaries`(`emp_no`, `salary`) VALUES (10003, 6000);
    INSERT INTO `salaries`(`emp_no`, `salary`) VALUES (10004, 6000);
    INSERT INTO `salaries`(`emp_no`, `salary`) VALUES (10001, 6000);
    INSERT INTO `salaries`(`emp_no`, `salary`) VALUES (10006, 6100);
    INSERT INTO `salaries`(`emp_no`, `salary`) VALUES (10005, 6900);
    INSERT INTO `salaries`(`emp_no`, `salary`) VALUES (10008, 7100);
    INSERT INTO `salaries`(`emp_no`, `salary`) VALUES (100010, 7400);
    INSERT INTO `salaries`(`emp_no`, `salary`) VALUES (100013, 7500);
    INSERT INTO `salaries`(`emp_no`, `salary`) VALUES (100014, 7500);
    INSERT INTO `salaries`(`emp_no`, `salary`) VALUES (100015, 7688);
    INSERT INTO `salaries`(`emp_no`, `salary`) VALUES (100018, 8000);
    INSERT INTO `salaries`(`emp_no`, `salary`) VALUES (100020, 8100);
    INSERT INTO `salaries`(`emp_no`, `salary`) VALUES (100028, 8200);
    INSERT INTO `salaries`(`emp_no`, `salary`) VALUES (100026, 8400);
    INSERT INTO `salaries`(`emp_no`, `salary`) VALUES (100035, 8500);
    INSERT INTO `salaries`(`emp_no`, `salary`) VALUES (100038, 8500);
    复制代码
    • 二、查询分析

    思路1、最容易想到的方法,就是查询到薪水的最大值和最小值。然后从薪水中排除掉这两个,计算平均值即可。

    复制代码
      select avg(salary)
        from salaries 
       where salary not in (
          (select min(salary) from salaries),
          (select max(salary) from salaries)
       ) ;
    复制代码

     

    思路2、使用开窗函数Max()  Over() 和 Min()  Over() 求出最大值,然后排除掉这两个,再计算平均值。

    复制代码
         select 
         avg(salary)
       from (
                select emp_no,salary, min(salary) over() min_sal, max(salary) over() max_sal  from salaries
             ) x
       where salary not in (min_sal,max_sal) ;
    复制代码

     

    思路3 、直接使用数学方法,平均值 = (求和-最大值-最小值)/ (总个数-2)

     

     select (sum(salary)-min(salary)-max(salary))/(count(*)-2)
      from salaries ;

     

     

    思路4、使用一次row_number() over 窗口函数和count() over 函数,count窗口函数统计表中所有记录数,使用row_number窗口函数按照薪水升序排列,排序结果 = 1 即为最小值,排序结果 = count出的结果 即为最大值。 

    复制代码
        select 
        avg(salary)
        from  
        (
        select 
        emp_no,
        salary,
        count(*) over()   num ,
        row_number() over(order by salary asc)   rn
        from  salaries )  T 
        where rn <> 1 and  rn  <> num ;
    复制代码

    说明:

    count(*)over() 求总计数,
    count(*)over(order by A...) 递加求计数,
    count(*)over(partition by A...) 分组求计数,
    count(*)over(partition by A...order by b...) 分组递加求计数

    查询看下这四个统计结果

     

    思路5、使用两次row_number() over 窗口函数,一个按照薪水升序,一个按照薪水降序,过滤掉第一个即去掉最大值和最小值。

    复制代码
        select 
        avg(salary)
        from  
        (
        select 
        emp_no,
        salary,
        row_number() over(order by salary desc)   rn_desc, 
        row_number() over(order by salary asc)   rn_asc 
        from  salaries )  T 
        where rn_desc > 1 and  rn_asc > 1 ;
    复制代码

     

    总结: 我们在执行这5个SQL语句后,会发现统计出的平均值不一样,原因就在于:薪水表中最高薪水的人和最低薪水的人都不止一个。

    前2个思路会将所有最高薪水和最低薪水全部去除,求得平均值。然而,思路3、4、5都只是去除一个最高薪水和一个最低薪水,然后求平均值。所以才导致计算出的结果不一致。

     

  • 相关阅读:
    免杀对抗-ShellCode上线+回调编译执行+混淆变异算法
    windows10安装MongoDB基础详细教程
    华为OD机试真题-事件推送-2023年OD统一考试(B卷)
    微信小程序:12.页面导航
    【自然语言处理】【实体匹配】用于实体匹配中blocking环节的深度学习:一个设计空间的探索
    Verilog 代码题练手(1)
    Tensorflow-图像处理
    Vue文档解析
    Python控制结构(空军工程大学)
    歌曲推荐《最佳损友》
  • 原文地址:https://www.cnblogs.com/yeyuzhuanjia/p/17607965.html