> 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/tu-jie-mian-shi-ti-ru-he-fen-xi-zhong-wei-shu.md).

# 图解面试题：如何分析中位数？

![](/files/-MWHpNS1YfxntxOSppK3)

学校每次考试完，都会有一个成绩表。例如，表中第 1 行表示编号为 1 的用户选择了 C++ 岗位，该科目考了 11001 分。

![](/files/-MWHpnFfsebGWNxgqgd6)

问题：写一个 sql 语句查询每个岗位的中位数位置的范围，并且按岗位升序排序，结果如下：

![](/files/-MWHq2KWFmHH8xRMRzaL)

解释:

第 1 行表示 C++ 岗位的中位数位置范围为 \[2,2]，也就是 2。因为 C++ 岗位总共 3 个人，是奇数，所以中位数位置为 2。

第 2 行表示 Java 岗位的中位数位置范围为 \[1,2]。因为 Java 岗位总共 2 个人，是偶数，所以要知道中位数，需要知道 2 个位置的数字，而因为只有 2 个人，所以中位数位置为 \[1,2]。

第 3 行表示前端岗位的中位数位置范围为 \[2,2]，也就是 2。因为前端岗位总共 3 个人，是奇数，所以中位数位置为 2。

**【解题步骤】**

1.要求每个岗位的中位数位置的范围，需要知道每个岗位的总数

那么，如何求每个岗位的总数呢？

看到 “**每个**”，要想到[《猴子 从零学会 SQL》](http://mp.weixin.qq.com/s?__biz=MzAxMTMwNTMxMQ==\&mid=2649249645\&idx=2\&sn=d1295b268eff974fe52a2c0f8bdadccb\&chksm=835fdf5db428564b7d42f733cdd45c6ce1aeeff0da8cdfac2f5047354bc815895f1a7fd282de\&scene=21#wechat_redirect)里讲过的，用分组汇总解决这类型问题。按岗位分组（group by），使用汇总函数 count() 得到岗位总数。

```sql
select 岗位,count(*) as 总数
from 成绩表
group by 岗位;
```

![](/files/-MWHr73G5TpU9bFR1IaL)

2.岗位总数又分为奇数和偶数两种情况。

![](/files/-MWHrB0wym5yvOvp0dRO)

岗位数有奇数也有偶数，需要分情况进行计算，可以使用 case 表达式

![](/files/-MWHra4dAK_B0Mh8F2SN)

如何判断奇偶呢？我们可以使用 % 或 mod 函数。

奇数，就是除以 2 的余数为 1，可以用下面两种方法表示

(1) 总数 % 2 = 1

(2) mod(总数, 2) = 1

偶数，就是除以 2 的余数为 0，可以用下面两种方法表示

(1) 总数 % 2 = 0

(2) mod(总数, 2) = 0

再结合求中位数起始位置的关系，使用 case 表达式：

![](/files/-MWHrrfdUiJWf-NPfbim)

![](/files/-MWHrutlYJ92Yw7rEM3p)

而 case 表达式中的**总数**是由前面使用第 1 步 count() 函数计算出来的，所以第 1 步要先于 case 语句运行，因为要把第 1 步的 sql 作为[子查询](http://mp.weixin.qq.com/s?__biz=MzAxMTMwNTMxMQ==\&mid=2649249645\&idx=2\&sn=d1295b268eff974fe52a2c0f8bdadccb\&chksm=835fdf5db428564b7d42f733cdd45c6ce1aeeff0da8cdfac2f5047354bc815895f1a7fd282de\&scene=21#wechat_redirect)。

```sql
select 岗位,
(case when 总数 % 2 = 1 then (总数+1)/2 else 总数/2 end) as 起始位置,
(case when 总数 % 2 = 1 then (总数+1)/2 else 总数/2+1 end) as 结束位置
from
(select 岗位,count(*) as 总数
from 成绩表
group by 岗位) as t;
```

最后按岗位升序排序，用排序子句（order by）

```sql
select 岗位,
(case when 总数 % 2 = 1 then (总数+1)/2 else 总数/2 end) as 起始位置,
(case when 总数 % 2 = 1 then (总数+1)/2 else 总数/2+1 end) as 结束位置
from
(select 岗位,count(*) as 总数
from 成绩表
group by 岗位) as t
order by 岗位;
```

![](/files/-MWHs6s_wgbrvoKGBF8F)

**【本题考点】**

1.考察如何将复杂问题拆解为简单问题，可以使用[多维度拆解分析方法](http://mp.weixin.qq.com/s?__biz=MzAxMTMwNTMxMQ==\&mid=2649248793\&idx=2\&sn=1fe86f3083bd9c8ad1a427898199c71c\&chksm=835fdc29b428553fabf47f23dc05f8f6c296cbcc06ce7e409ff712e81141fbf9ad0aa9b2b99d\&scene=21#wechat_redirect)，例如本案例中问题拆解问 3 步：

1）将中位数位置用公式表示出来

2）计算出每个岗位的总数

3）分情况统计总数的奇数、偶数

2.如何用 % 或 mod 函数判断奇偶

3.每个问题，要想到分组汇总来解决

4.多条件判断问题，要想到用 case 表达式

5.考查 sql 的运行顺序和子查询

**【举一反三】**

以下是某班同学的科目成绩表，查询每门科目的中位数位置的范围，并且按科目升序排序。

![](/files/-MWHsAkiMLARM07u7jdN)

```
select 科目,
(case when 总数 % 2 = 1 then (总数+1)/2 else 总数/2 end) as 起始位置,
(case when 总数 % 2 = 1 then (总数+1)/2 else 总数/2+1 end) as 结束位置
from
(select 科目,count(*) as 总数
from 科目成绩表
group by 科目) as t
order by 科目;
```

![](/files/-MWHsFWtHHC_-MmerQvX)

第 1 行表示数学科目的中位数位置范围为 \[2,2]，也就是 2。因为数学科目总共 3 个人，是奇数，所以中位数位置为 2。

第 2 行表示语文科目的中位数位置范围为 \[2,3]。因为 Java 岗位总共 4 个人，是偶数，所以要知道中位数，需要知道 2 个位置的数字，而因为只有 4 个人，所以中位数位置为 \[2,3]。

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

![](/files/-MWHsMekI5XfiWp5ibG9)
