今天有同事问我一道关于数据库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都只是去除一个最高薪水和一个最低薪水,然后求平均值。所以才导致计算出的结果不一致。