> For the complete documentation index, see [llms.txt](https://houzidata.gitbook.io/sql/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://houzidata.gitbook.io/sql/di-5-zhang-xiang-mu-shi-zhan/zi-jie-tiao-dong-mian-shi-ti-ni-de-ping-jun-xin-shui-shi-duo-shao.md).

# 字节跳动面试题：你的平均薪水是多少？

![](/files/-MWPVWhX91VFKKCDZfDN)

薪水表中记录了员工的编号，所在部门编号，和薪水。

![](/files/-MWPVasYFtAbnEt0BsoN)

查询出每个部门除去最高、最低薪水后的平均薪水，并保留整数。（字节跳动面试题）

**【解题步骤】**

**1. 如何找出最高、最低薪水？**

要求每个部门除去最高、最低薪水后的的平均薪水，所以应该查询出每个部门的最高、最低工资。

所以需要按每组来排名薪水，**既要排名，又要分组的问题**，需要用[窗口函数](http://mp.weixin.qq.com/s?__biz=MzAxMTMwNTMxMQ==\&mid=2649249645\&idx=2\&sn=d1295b268eff974fe52a2c0f8bdadccb\&chksm=835fdf5db428564b7d42f733cdd45c6ce1aeeff0da8cdfac2f5047354bc815895f1a7fd282de\&scene=21#wechat_redirect)来解决。

窗口函数的基本语法如下：

```sql
<窗口函数> over (partition by <用于分组的列名>                
order by <用于排序的列名>)
```

语法中 <窗口函数> 的位置，可以放以下两种函数：

1） 专用窗口函数，包括 rank, dense\_rank, row\_number 等专用窗口函数。

2） 聚合函数，如 sum. avg, count, max, min 等

在该题中，我们需要对部门进行分组，并对薪水进行排序：

```sql
select *,   
      rank() over (partition by 部门编号 
                   order by 薪水 desc) as ranking
from 薪水表;
```

查询结果如下，因为是降序排列，排名为 1 的是每个部门的最高薪水。

![](/files/-MWPVnNrYqjOHji33MZF)

我们还需要再用一次窗口函数求出每个部门的最低薪水，也就是升序排列时，排名为 1 的是每个部门的最低薪水

```sql
select *,   
  rank() over (partition by 部门编号 order by 薪水 desc) as rank_1,
  rank() over (partition by 部门编号 order by 薪水) as rank_2
from 薪水表;
```

查询结果如下。下图 rank\_1 列数值为 1（红色框）的是每个部门的最高薪水，rank\_2 列数值为 1（蓝色框）的是每个部门的最低薪水。

![](/files/-MWPVw_6cd6uOqG0puQb)

**2. 如何去掉最高和最低薪水？**

用 where 子句来筛选就可以了，where rank\_1 > 1 and rank\_2 > 1

![](/files/-MWPW2uh52zO7xRN1lya)

```sql
select *,   
  rank() over (partition by 部门编号 order by 薪水 desc) as rank_1,
  rank() over (partition by 部门编号 order by 薪水) as rank_2
from 薪水表
where rank_1 >1 and rank_2 >1;
```

但是这样运行会出错，因为按照 [sql 的运行顺序](http://mp.weixin.qq.com/s?__biz=MzU5NTc1ODI5Mw==\&mid=2247484035\&idx=1\&sn=f6cc38ffb91396061ab7f101257262db\&chksm=fe6c5f39c91bd62f88162ea5ef75a9b757139cad11f02ca6f3de2db931cc89e621921da0dc2d\&scene=21#wechat_redirect)，会先运行 from 和 where 子句，最后才运行 select 子句。

而 rank\_1 和 rank\_2 在 select 子句中，是最后运行的。所以运行时 where 子句中的 rank\_1 和 rank\_2 是不存在的。

所以，我们需要将前面的查询结果作为[子查询](http://mp.weixin.qq.com/s?__biz=MzAxMTMwNTMxMQ==\&mid=2649249645\&idx=2\&sn=d1295b268eff974fe52a2c0f8bdadccb\&chksm=835fdf5db428564b7d42f733cdd45c6ce1aeeff0da8cdfac2f5047354bc815895f1a7fd282de\&scene=21#wechat_redirect)后，再用 where 子句：

```sql
select *
from (
  select *,
  rank() over (partition by 部门编号 order by 薪水 desc) as rank_1,
  rank() over (partition by 部门编号 order by 薪水) as rank_2
  from 薪水表
 ) as a
where a.rank_1 >1 and a.rank_2 >1;
```

查询结果如下，此时已经是每个部门去掉最高、最低薪水后的结果了。

![](/files/-MWPWDYg9acK_-MdhY3O)

**3. 查询每个部门除去最高、最低薪水的平均薪水**

看到 “每个” 这样的问题，要想到用分组（group by），平均薪水使用 avg 函数。

另外，题目还要求薪水保留整数。保留整数即保留 0 位小数，可以用 format 函数：

```sql
format(N,D)
N是要格式化的数字
D是要舍入的小数位数。
```

```sql
select a.部门编号,format(avg(a.薪水),0) as 平均薪水
from
  (
  select *,
  rank() over (partition by 部门编号 order by 薪水 desc) as rank_1,
  rank() over (partition by 部门编号 order by 薪水) as rank_2
  from 薪水表
  ) as a
where a.rank_1 >1 and a.rank_2 >1
group by a.部门编号;
```

查询结果如下。

![](/files/-MWPWPog9cLfyTPvbexi)

**【本题考点】**

1. 考察解决复杂问题的能力，可以使用[逻辑树分析方法](http://mp.weixin.qq.com/s?__biz=MzAxMTMwNTMxMQ==\&mid=2649248793\&idx=2\&sn=1fe86f3083bd9c8ad1a427898199c71c\&chksm=835fdc29b428553fabf47f23dc05f8f6c296cbcc06ce7e409ff712e81141fbf9ad0aa9b2b99d\&scene=21#wechat_redirect)，将复杂问题拆解问简单的子问题。
2. 考查 sql 的运行顺序和[子查询](http://mp.weixin.qq.com/s?__biz=MzAxMTMwNTMxMQ==\&mid=2649249645\&idx=2\&sn=d1295b268eff974fe52a2c0f8bdadccb\&chksm=835fdf5db428564b7d42f733cdd45c6ce1aeeff0da8cdfac2f5047354bc815895f1a7fd282de\&scene=21#wechat_redirect)
3. 遇到既要分组，又要排名的问题，要想到使用[窗口函数](http://mp.weixin.qq.com/s?__biz=MzAxMTMwNTMxMQ==\&mid=2649249645\&idx=2\&sn=d1295b268eff974fe52a2c0f8bdadccb\&chksm=835fdf5db428564b7d42f733cdd45c6ce1aeeff0da8cdfac2f5047354bc815895f1a7fd282de\&scene=21#wechat_redirect)
4. 考查平均数的计算以及结果保留几位小数

**【举一反三】**

如图是某班 6 名同学的成绩：

![](/files/-MWPWSrT046pWSnitxmL)

请你写一个 sql 语句查询该 6 名同学的成绩中除去最高、最低分的后的平均分数，并保留 2 位小数。

```sql
select format(avg(a.成绩),2) as 平均成绩
from
  (
  select *,
  rank() over (order by 成绩 desc) as rank_1,
  rank() over (order by 成绩) as rank_2
  from 成绩表
  ) as a
where a.rank_1 >1 and a.rank_2 >1;
```

![](/files/-MWPWchkurjOLl0ZxUo_)

![](/files/-MWPWgRx9F3HlAQFaht-)

[推荐：如何从零学会 sql？](http://mp.weixin.qq.com/s?__biz=MzAxMTMwNTMxMQ==\&mid=2649249645\&idx=2\&sn=d1295b268eff974fe52a2c0f8bdadccb\&chksm=835fdf5db428564b7d42f733cdd45c6ce1aeeff0da8cdfac2f5047354bc815895f1a7fd282de\&scene=21#wechat_redirect)

![](/files/-MWPWkrmBPhmhfcVfHvY)
