【题目】
雇员表中是员工的基本信息:
问题:查找按名字的首字母升序排列后所在的行数为奇数行的雇员的名字。
如上图所示,这四位雇员的名字首字母分别为G,B,K,A。升序排列后为A,B,G,K,因此奇数行1,3行对应的雇员为名字首字母是A和G开头的。
输出格式:
【解题步骤】
1.排名问题
该题的关键在于如何判断某行按名字首字母排序后的该行的序号以及该序号是奇数还是偶数,我们先将题目简化:
如上图,该表按照字母升序排列后应该为:
关键在于如何得出排序后字母对应的序号,这其实是排名问题。根据《猴子 从零学会SQL》里讲过的排名问题,可以使用窗口函数。
专用排名的窗口函数rank, dense_rank, row_number有什么区别呢?
它们的区别我举个例子,你们一下就能看懂:
代码语言:javascript复制1 select *,
2 rank() over (order by 成绩 desc) as ranking,
3 dense_rank() over (order by 成绩 desc) as dese_rank,
4 row_number() over (order by 成绩 desc) as row_num
5 from 班级表;
查询结果如下:
从上面的结果可以看出:
rank函数:这个例子中是5位,5位,5位,8位,也就是如果有并列名次的行,会占用下一名次的位置。比如正常排名是1,2,3,4,但是现在前3名是并列的名次,结果是:1,1,1,4。 dense_rank函数:这个例子中是5位,5位,5位,6位,也就是如果有并列名次的行,不占用下一名次的位置。比如正常排名是1,2,3,4,但是现在前3名是并列的名次,结果是:1,1,1,2。 row_number函数:这个例子中是5位,6位,7位,8位,也就是不考虑并列名次的情况。比如前3名是并列的名次,排名是正常的1,2,3,4。
这三个函数的区别如下:
根据题目要求的排名规则,我们要查找按名字的首字母升序排列后所在的行数为奇数行的雇员的名字。
要知道排序后的行数,所以数字不能有相同的,所以这里我们使用row_num函数。
代码语言:javascript复制1 select row_number() over (order by 字母) as 序号,
2 字母
3 from 字母表;
2.找出奇数
排序后的序号已知后,判断奇偶数也变得容易了。
奇数行,就是序号除以2的余数为1,可以用下面两种方法表示
(1) 序号 % 2 = 1
(2) mod(序号,2) = 1
偶数行则将上面的余数1改为0即可。
在上面的sql上加上where子句:
代码语言:javascript复制1 select row_number() over (order by 字母) as 序号, 字母
2 from 字母表
3 where mod(序号,2) = 1;
这样写有你能发现有什么问题吗?
由于sql的运行顺序,会先运行where子句,再运行select子句。运行where子句的时候,没有“序号”这一列,就会报错。
因此无法直接在后面加上:where mod(序号,2) = 1。需要需要将有序号的查询结果作为临时表(也就是用子查询),修改后的sql如下:
代码语言:javascript复制1 with 临时表
2 as
3 (select row_number() over (order by 字母) as 序号,字母
4 from 字母表)
5 select 字母
6 from 临时表
7 where mod(序号,2) = 1;
3.回答题目要求
题目中的雇员表实际也只是比较名字这一列的字母,简化后就是上述的问题。
要求查找按名字的首字母升序排列后所在的行数为奇数行的雇员的名字(方法相同):
代码语言:javascript复制1 with 临时表
2 as(select row_number() over (order by 名字) as 序号,名字
3 from 雇员表)
4 select 名字
5 from 临时表
6 where mod(序号,2) = 1;
【本题考点】
1.能将复杂的问题简化为简单的问题,如本题将问题简化后,其实就是排名问题
2.排名问题,要想到使用窗口函数实现。在工作我们经常会遇到排名问题,比如对用户搜索对关键词按搜索次数排名,对商品按销售量排名,对员工按kpi排名选出优秀员工。
3.考查如何用mod函数或者%判断奇偶。
【举一反三】
学生表中是学生的基本信息:
问题:查找学号为偶数的学生的全部信息。
代码语言:javascript复制1 select *
2 from 学生表
3 where mod(学号,2) = 0;
推荐:如何从零学会sql?