HiveSql面试题12--如何分析去掉最大最小值的平均薪水(字节跳动)

目录

0 问题描述

1 数据准备

2 数据分析

3 小结


0 问题描述

  • 薪水表中是员工薪水的基本信息,包括雇员编号,部门编号和薪水
  • 第1行表示雇员编号为10001的员工在1号部门,薪水为60117元;
  • 第2行表示雇员编号为10002的员工在2号部门,薪水为92102元;
  • ...
  • 第10行表示雇员编号为10010的员工在1号部门,薪水为76884元

问题:查询每个部门除去最高、最低薪水后的平均薪水,并保留整数。 

1 数据准备

(1)数据

基本数据
雇员编号 部门编号 薪水
10001 1 60117
10002 2 92102
10003 2 86074
10004 1 66596
10005 1 66961
10006 2 81046
10007 2 94333
10008 1 75286
10009 2 85994
10010 1 76884

(2)建表SQL

drop table if exists dan_test.salary

CREATE TABLE dan_test.salary (

emp_num string ,

dep_num string ,

salary string

)

ROW format delimited FIELDS TERMINATED BY ",";

(3)加载数据

load data local inpath "/home/centos/dan_test/salary.txt" into table salary;

 (4) 查询数据

2 数据分析

目标:需要查询每个部门除去最高、最低薪水后的平均薪水,并保留整数。 

三个关键点信息:

  •   (1) 每个部门(按部门分组)
  • (2)除去最高、最低薪水。(需要先求出最高,最低的薪水,并进行过滤,本题的关键)
  • (3)在(1)和(2)基础上求平均值,并保留整数

 思路1:找出部门中最高、最低薪水并进行过滤。采用窗口函数row_number()对按部门分组,薪水排序后的数据进行标记

SQL如下:

select emp_num
      ,dep_num
	  ,salary
	  ,row_number() over(partition by dep_num order by salary) as rn1 --标记最小
	  ,row_number() over(partition by dep_num order by salary desc) rn2 --标记最大
from salary

查询结果如下:

根据需求,过滤出中间结果,即rn1 >1 和rn2 >1 条件同时成立。当存在多个窗口结果集,且显示的字段参与窗口运算,则该字段以最后一个窗口函数为准,如本题salary显示结果是以最后一个窗口结果显示的。 最终SQL如下

select *
from(
select emp_num
      ,dep_num
	  ,salary
	  ,row_number() over(partition by dep_num order by salary) as rn1 --标记最小
	  ,row_number() over(partition by dep_num order by salary desc) rn2 --标记最大
from salary
) t
where rn1 > 1 and rn2 > 1

思路2:为了过滤最大、最小,我们可以只进行一次排序,先求出分组总行数,然后对salary进行分组升序排序,那么我们需要的结果就是,1<rn<cn。具体SQL如下:

select emp_num
      ,dep_num
	  ,salary
	  ,count(1) over(partition by dep_num) as cn --求出总行数
	  ,row_number() over(partition by dep_num order by salary ) rn --按照薪水升序排序
from salary

最终过滤出需要的结果如下:

select *
from(
select emp_num
      ,dep_num
	  ,salary
	  ,count(1) over(partition by dep_num) as cn --求出总行数
	  ,row_number() over(partition by dep_num order by salary ) rn --按照薪水升序排序
from salary
) t
where rn > 1 and rn < cn

 求得最终平均值的SQL如下:

---方法1
select t.dep_num,round(avg(t.salary),0) as avg_salary
from
  (
  select *,
  row_number() over (partition by dep_num order by salary desc) as rn1,
  row_number() over (partition by dep_num order by salary) as rn2
  from salary
  ) t
where t.rn1 > 1 and t.rn2 > 1
group by t.dep_num;

--------方法2
select t.dep_num,round(avg(t.salary),0) as avg_salary
from
  (
  select emp_num
        ,dep_num
	    ,salary
	    ,count(1) over(partition by dep_num) as cn --求出总行数
	    ,row_number() over(partition by dep_num order by salary ) rn --按照薪水升序排序
  from salary
  ) t
where t.rn > 1 and t.rn < t.cn
group by t.dep_num;

计算结果如下:

方法3:公式法。按照求平均值的正常思路:(sum(salary)-max(salary) -min(salary)) / count(salary) -2  --按照部门分组。

(1)先按每个部门求最大薪水值、最小薪水值、薪水值总和总次数。具体SQL如下:

select 
       dep_num
	  ,count(salary) as cn --求出总个数
	  ,max(salary) as max_salary --salary最大值
	  ,min(salary) as min_salary --salary最小值
	  ,sum(salary) as sum_salary --salary总和
from salary
group by dep_num

 求的结果如下:

(2)求每个部门去除最大、最小值后的平均值 (公式法)。

此时按照部门进行分组后,由于未在分组中的字段的获取必须放在聚合函数中才能被使用,因而我们采用max()函数来获取其余字段,但会不会影响结果呢?其实是不会影响的,因为上一步的结果返回值都是聚合过后的,每个部门只有一条数据,每个字段也只有一个值,因而你无论使用了max(),min(),sum()等函数其结果都是一样的。这也是一种技巧,当分组后如果这一组中只有一个值时,为了提取该字段值,我们可以使用max(),min()函数进行提取,聚合函数往往也具有过滤NULL值的作用,利用这一特性,在写SQL时往往为上层查询提供了很多便利。

最终的SQL 如下:

select dep_num
    ,(max(sum_salary) - max(max_salary) -max(min_salary)) / (max(cn) -2) as avg_salary--使用max()函数提取非group by组中的字段值,供上层计算
from (
select 
       dep_num
	  ,count(salary) as cn --求出总个数
	  ,max(salary) as max_salary --salary最大值
	  ,min(salary) as min_salary --salary最小值
	  ,sum(salary) as sum_salary --salary总和
from salary
group by dep_num
) t
group by dep_num

最终结果如下:

3 小结

本文使用的知识点总结:

  • (1)排名函数的作用及使用技巧。row_number()
  • (2)round()函数的使用。
  • (3)如何排除最大、最小值的技巧
  • (4)非group by字段中值如何获取的技巧

猜你喜欢

转载自blog.csdn.net/godlovedaniel/article/details/112372060
今日推荐