SQL面试经典50题

题目及数据来源: https://zhuanlan.zhihu.com/p/38354000

以下记录个人认为有所难度的题目和解答

1. 查询两门以上不及格课程的同学的学号及其平均成绩

原回答有误:

select 学号, avg(成绩) as 平均成绩
from score
where 成绩 <60
group by 学号
having count(课程号)>2;

按此语句查询出的仅是符合条件的学生不及格课程的平均成绩,而不是符合条件的学生所有课程的平均成绩。正确写法如下:

select 学号,avg(成绩) 平均成绩
from score 
where 学号 in 
(select 学号 from score where 成绩<60 group by 学号 having count(*) > 2)
group by 学号;

2. 查询所有课程成绩小于60分的学生学号、姓名

原答案有误:

select 学号,姓名
from student
where  学号 in (
select 学号 
from student  #应为score表
where 成绩 < 60);

按此语句查询出的是至少一门课程成绩小于60分的学生学号、姓名,而非所有课程成绩小于60分的学生学号、姓名。以下为正确写法:

select s.学号,姓名
from student s
join score sc
on s.学号=sc.学号
group by s.学号
having max(成绩)<60;

此外,子查询的效率一般低于关联查询,所以尽量用表联接来代替不必要的子查询。

3.查询所有生日在1990年的学生学号、姓名

原答案:

select 学号,姓名
from student
where year(出生日期)=1990;

用Year函数后会对每行数据都进行运算,在数据量大时运算效率会变得很低。所以实际工作中直接设定起止时间点更简单高效。

select 学号,姓名
from student
where 出生日期 between '1990-01-01' and '1990-12-31';

4.查询出每门课程的及格人数和不及格人数

要求输出如下:

select 课程号,
sum(case when 成绩>=60 then 1 
	 else 0 
    end) as 及格人数,
sum(case when 成绩 <  60 then 1 
	 else 0 
    end) as 不及格人数
from score
group by 课程号;

理解过程:
首先,score表的每一行都将执行一遍case函数,得到相应的逻辑判断结果。也即,如果按照以下语句,将会得到如下输出:

select 课程号,
case when 成绩>=60 then 1 else 0 end as 及格人数,
case when 成绩 <  60 then 1 else 0 end as 不及格人数
from score;

然后按照课程号来聚合统计每门课的及格人数与不及格人数。综合就得到了上面的解法

5. 查询各个课程的分数段分布

要求输出如下:

select c.课程号,课程名称,
sum(case when 成绩<=100 and 成绩>=85 then 1 else 0 end) as '[100-85]',
sum(case when 成绩<85 and 成绩>=70 then 1 else 0 end) as '[85-70]',
sum(case when 成绩<70 and 成绩>=60 then 1 else 0 end) as '[70-60]',
sum(case when 成绩<60 then 1 else 0 end) as '[<60]'
from score sc
right join course c
on sc.课程号=c.课程号
group by c.课程号

6. 行列互换

将score表行列互换,转换成以下形式:

select 学号,
max(case 课程号 when '0001' then 成绩 else 0 end) as '课程号0001',
max(case 课程号 when '0002' then 成绩 else 0 end) as '课程号0002',
max(case 课程号 when '0003' then 成绩 else 0 end) as '课程号0003'
from score
group by 学号;

解题思路:
首先,score表的每一行都将执行一遍case函数,得到相应的逻辑判断结果。也即,如果按照以下语句,将会得到如下输出:

select 学号,
(case 课程号 when '0001' then 成绩 else 0 end) as '课程号0001',
(case 课程号 when '0002' then 成绩 else 0 end) as  '课程号0002',
(case 课程号 when '0003' then 成绩 else 0 end) as '课程号0003'
from score;

然后按照如上图所示,按小方块进行分组与max聚合,就将得到最终结果。

7. 查询不同课程成绩相同的学生的学生编号、课程编号、学生成绩

select distinct s1.学号,s1.课程号,s1.成绩
from score s1
join score s2
on s1.学号=s2.学号
where s1.课程号!=s2.课程号 and s1.成绩=s2.

正确输出结果如下:

如果不加distinct,那么由于每个课程都与另外两个课程相等,结果就会出现如下重复:

8.查询课程编号为“0001”的课程比“0002”的课程成绩高的所有学生的学号

原答案:

select a.学号  
​from 
(select 学号 ,成绩 from score where 课程号=01) as a
inner join 
(select 学号 ,成绩 from score where 课程号=02) as b
on a.学号 =b.学号 
inner join student c on c.学号 =a.学号  #没有必要连接student表
where a.成绩 >b.成绩 ;

也可以通过自连接来实现:

select s1.学号
from score s1
join score s2
on s1.学号=s2.学号
where s1.课程号='0001' and s2.课程号='0002' and s1.成绩>s2.成绩

9. 查询学过编号为“0001”的课程并且也学过编号为“0002”的课程的学生的学号、姓名

如同上题,同样有两种写法:

select s1.学号,姓名
from 
(select 学号 from score where 课程号='0001') s1
join
(select 学号 from score where 课程号='0002') s2
on s1.学号=s2.学号
join student s
on s2.学号=s.学号
select s1.学号,姓名
from score s1
join score s2
on s1.学号=s2.学号
join student s
on s.学号=s2.学号
where s1.课程号='0001' and s2.课程号='0002';
/* where条件也可以写为: s1.课程号='0002' and s1.课程号='0002', 但不能写为: (s1.课程号='0001' and s2.课程号='0002) or (s1.课程号='0002' and s1.课程号='0002'),这将会导致重复 */

10. 查询学过“孟扎扎”老师所教的所有课的同学的学号、姓名

原答案有误:

select s.学号 ,s.姓名
from student as s  
inner join score as a  
​​on s.学号 =a.学号 
inner join  course  b on a.课程号 =b.课程号
inner join  teacher c  on b.教师号 = c.教师号
where c.教师姓名 ='孟扎扎';

按此语句,所有只要上过孟老师一节课的学生,其信息就会被查询出来,不符合题目中要求的,查询上过孟老师所有课的学生的信息。正确的写法应该是,首先查出学过孟老师课程的同学学号(包含只学一门),同时统计每个同学符合这个条件的有几条记录,也即其上过孟老师的课的数量,然后将这个数量与孟老师的代课数量进行对比,筛选出最后的答案。

#创建孟扎扎所代课程的课程号视图 mc
create view mc as 
(select 课程号
from course c 
join teacher t 
on c.教师号=t.教师号
where 教师姓名='孟扎扎');

#查询选过mc中所有课程的学生学号、姓名
select a.学号,姓名
from 
(select 学号,count(*) 课程数量
from score
where 课程号 in (select 课程号 from mc)
group by 学号
) a
join student s 
on a.学号=s.学号
where a.课程数量=
(select count(*) from mc)

11. 查询没学过“孟扎扎”老师课的学生的学号、姓名

此题先查询出上过孟老师课的学生,再在所有学生中将这部分学生排除掉即可

select 学号,姓名
from student 
where 学号 not in
(
select 学号 from score sc
join course c
on sc.课程号=c.课程号
join teacher t 
on c.教师号=t.教师号
where 教师姓名='孟扎扎'
);

12. 按学号列出每个同学的各科成绩与平均成绩,并按平均成绩从高到低显示

要求输出如下:

select 学号,
max(case 课程名称 when '语文' then 成绩 else 0 end) 语文,
max(case 课程名称 when '数学' then 成绩 else 0 end) 数学,
max(case 课程名称 when '英语' then 成绩 else 0 end) 英语,
avg(成绩) 平均成绩
from score sc 
join course c 
on sc.课程号=c.课程号
group by 学号

13.查询每门课程成绩排名前两位的学生姓名

要求按如下输出:

select 课程号,姓名,成绩,排名
from 
(select 课程号,学号,成绩,
row_number() over (partition by 课程号 order by 成绩 desc) 排名
from score) rk
join student s 
on rk.学号=s.学号
where 排名<3
order by 课程号;

注意窗口函数一般只用于select后,所以以下写法是不支持的:

select 课程号,姓名,成绩,
row_number() over (partition by 课程号 order by 成绩 desc) 排名
from score sc
join student s
on sc.学号=s.学号
where 排名<3

窗口函数的使用参考: 通俗易懂的学会:SQL窗口函数

本文地址:https://blog.csdn.net/Zita_11/article/details/107268016