SQL 50道练习题 解答过程

SQL 50道练习题 解答过程

    • 初始表格数据
    • 习题附解答

博主使用的是MySQL8.0.13 + workbench,如果是使用Oracle或者SQL Server可能会有语言差异或者是MySQL的不同版本也许会有差异。
题目来源及答案部分参考:
https://blog.csdn.net/zhang945254064/article/details/80305602
https://zhuanlan.zhihu.com/p/32137597
再次一并感谢博主和答主,为大家提供学习素材。

初始表格数据

通过运行代码:

# SQL CODE
# Create 4 tables and insert data into them
create table Student
  (SID varchar(10),
   Sname varchar(10),
   Sage datetime,
   Ssex varchar(10));
insert into Student values('01' , '赵雷' , '1990-01-01' , '男');
insert into Student values('02' , '钱电' , '1990-12-21' , '男');
insert into Student values('03' , '孙风' , '1990-05-20' , '男');
insert into Student values('04' , '李云' , '1990-08-06' , '男');
insert into Student values('05' , '周梅' , '1991-12-01' , '女');
insert into Student values('06' , '吴兰' , '1992-03-01' , '女');
insert into Student values('07' , '郑竹' , '1989-07-01' , '女');
insert into Student values('08' , '王菊' , '1990-01-20' , '女');

create table Course 
(CID varchar(10),
 Cname varchar(10),
 TID varchar(10));
insert into Course values('01' , '语文' , '02');
insert into Course values('02' , '数学' , '01');
insert into Course values('03' , '英语' , '03');

create table Teacher 
(TID varchar(10),
 Tname nvarchar(10));
insert into Teacher values('01' , '张三');
insert into Teacher values('02' , '李四');
insert into Teacher values('03' , '王五');
create table SC
(SID varchar(10),
 CID varchar(10),
 score decimal(18,1));
insert into SC values('01' , '01' , 80);
insert into SC values('01' , '02' , 90);
insert into SC values('01' , '03' , 99);
insert into SC values('02' , '01' , 70);
insert into SC values('02' , '02' , 60);
insert into SC values('02' , '03' , 80);
insert into SC values('03' , '01' , 80);
insert into SC values('03' , '02' , 80);
insert into SC values('03' , '03' , 80);
insert into SC values('04' , '01' , 50);
insert into SC values('04' , '02' , 30);
insert into SC values('04' , '03' , 20);
insert into SC values('05' , '01' , 76);
insert into SC values('05' , '02' , 87);
insert into SC values('06' , '01' , 31);
insert into SC values('06' , '03' , 34);
insert into SC values('07' , '02' , 89);
insert into SC values('07' , '03' , 98);

习题附解答

  1. 查询同时存在" 01 “课程和” 02 "课程的学生信息情况
select Student.* from Student
where SID in
(select a.SID from (select SID from SC where SC.CID = '01' ) as a, 
(select SID from SC where SC.CID = '02' ) as b
where a.SID = b.SID)

原理是选出两张表:a表中是选择课程"01"的学生ID,b表中是选择课程"02"的学生ID将两张表通过学生ID内连接可以得到同时选择两门课的学生ID。

  1. 查询选择" 01 "课程的学生的“01”课程和“02”课程的成绩(02成绩如果不存在,用Null表示)
select Student.SID, Student.Sname, k.score_01, k.score_02 from Student,
(select a.SID as SID, score_01, score_02 from 
(select SID, score as score_01 from SC where CID ='01') as a left join
(select SID, score as score_02 from SC where CID = '02') as b on a.SID = b.SID) as k
where Student.SID = k.SID

原理类似第一题但是注意这里使用的是left join来保证所有选择“01”课程的学生都包括进来。

  1. 查询学生中没有选" 01 “课程选了” 02 "课程的学生成绩情况
# 解法一:not in
select * from SC where CID = '02' and SID not in 
(select SID from SC where CID ='01')
# 解法二:not exists
select SID from SC where CID = '02' and
not exists(select * from SC as a where a.CID = '01' and a.SID = SC.SID)

这里有两个条件:选择了课程“02”但是没有选课程“01”

  1. 查询平均成绩大于等于 60 分的同学的学生编号,学生姓名,平均成绩
select Student.SID, Student.Sname, avg_score from Student inner join
(select SID, avg(score) as avg_score from SC group by SID having avg(score) > 60) as k
on Student.SID = k.SID

这里需要注意到group by和having语句**(不能使用where语句来引导条件)**。

  1. 查询在 SC 表存在成绩的学生信息
# 解法一: in
select * from Student where SID in
(select distinct SID from SC)
# 解法二: exists
select * from Student where exists(select * from SC where SC.SID = Student.SID)
  1. 查询所有同学的学生编号、学生姓名、选课总数、所有课程的总成绩(没成绩的显示为null)
select Student.SID, Sname, course_num, total_score from Student left join
(select SID, count(*) as course_num, sum(score) as total_score from SC group by SID) as k
on Student.SID = k.SID
  1. 查有成绩的学生信息(第5题,解法二)
select * from Student where exists( select * from SC where SC.SID = Student.SID )

Exists 用于判断:对于每一个Student中的一行数据(一个SID),如果这个( select * from SC where SC.SID = Student.SID)表格不为空,那么这一行数据会被选择在最后的结果中。

  1. 查询「李」姓老师的数量
select count(*) from Teacher where Tname like '李%'

这里考察通配符以及like判断语句。

  1. 查询学过「张三」老师授课的同学的信息
select Distinct Student.* from Student, SC, course, Teacher where
Student.SID = SC.SID and SC.CID = course.CID and course.TID = Teacher.TID and Teacher.Tname = '张三'

另外一种方法是不断地使用in来嵌套判断,但是这里使用多重条件,代码看起来会清晰一点。

  1. 查询没有学全所有课程的同学的信息
select Student.* from Student where SID not in
(select SID from SC group by SID having count(*) = (select count(*) from Course))

往反面思考,学全所有课程的同学的课程数量应当是course表中课程数量。
有一种错误的做法是,

select * from Student where SID in
(select SID from SC group by SID having count(*) < (select count(*) from Course)) 

这会漏选掉一门课都没有选过的同学。

  1. 查询至少有一门课与学号为" 01 "的同学所学相同的同学的信息
select * from Student where SID <> '01' and SID in
(select distinct SID from SC where CID in (select CID from SC where SID = '01'))
  1. 查询和" 01 "号的同学学习的课程完全相同的其他同学的信息
select Student.* from Student where SID <>'01' and SID in
(select SID from SC where CID in (select CID from SC where SID = '01') 
 group by SID having count(*) = (select count(*) from SC where SID = '01'))

这题的逻辑比较复杂,在选课范围是“01”号同学的课程里面,所选的课程数目与“01”号同学相同的。可以视为和“01”号同学学了完全相同的课程。

  1. 查询没学过"张三"老师讲授的任一门课程的学生姓名
select * from Student where SID not in
 (select Distinct SID from SC, Course, Teacher
 where SC.CID = course.CID and Course.TID = Teacher.TID and Teacher.Tname = '张三')

想反面的问题,找到选过“张三”老师讲授的课程的学生ID,用not in来排除。

  1. 查询两门及其以上不及格课程的同学的学号,姓名及其平均成绩
select Student.SID, Student.Sname, avg_score from Student inner join 
(select SID, avg(score) as avg_score from SC
where SID in 
(select SID from SC where score < 60 group by SID having count(*) > 1) group by SID) as a 
on Student.SID = a.SID
  1. 检索" 01 "课程分数小于 60,按分数降序排列的学生信息
select * from Student inner join 
(select SID, score as score_01 from SC where CID = '01' and score < 60) as a
on Student.SID = a.SID order by score_01 DESC

注意order by语句和DESC关键词的使用。

  1. 按平均成绩从高到低显示所有学生的所有课程的成绩以及平均成绩
select SC.SID, SC.score,SC.CID,avg_score from SC inner join
(select SID, avg(score) as avg_score from SC group by SID) as a
on SC.SID = a.SID order by avg_score DESC

返回的结果中,没有一门课没选的“08”号同学。

  1. 查询各科成绩最高分、最低分和平均分。以如下形式显示:课程 ID,课程 name,最高分,最低分,平均分,及格率,中等率,优良率,优秀率。及格为>=60,中等为:70-80,优良为:80-90,优秀为:>=90及格为>=60,中等为:70-80,优良为:80-90,优秀为:>=90
select Course.CID,Course.Cname,max_score,min_score, avg_score ,及格率, 中等率, 优良率, 优秀率 from Course inner join
(select CID, max(score) as max_score, min(score) as min_score, avg(score) as avg_score, sum(case when score >=60 then 1 else 0 end)/count(*) as 及格率, sum(case when score >=70 and score < 80 then 1 else 0 end)/count(*) as 中等率,sum(case when score >=80 and score < 90 then 1 else 0 end)/count(*) as 优良率, sum(case when score >=90 then 1 else 0 end)/count(*) as 优秀率 from SC group by CID) as a
on Course.CID = a.CID

需要了解case when语句的用法。

  1. 按各科成绩进行排序,并显示排名, Score 重复时保留名次空缺
select sc.SID, sc.CID, sc.score, row_number()
over(partition by CID order by score DESC) as rank_num from SC
  1. 按各科成绩进行排序,并显示排名, Score 重复时合并名次
select sc.SID, sc.CID, sc.score, rank() over(partition by CID order by score DESC) as rank_num from SC
  1. 查询学生的总成绩,并进行排名,总分重复时保留名次空缺
select SID, total_score, row_number() over (order by total_score DESC) as rank_num from (select SID, sum(score) as total_score from SC group by SID) as a
  1. 查询学生的总成绩,并进行排名,总分重复时不保留名次空缺
select SID, total_score, row_number() over (order by total_score DESC) as rank_num from (select SID, sum(score) as total_score from SC group by SID) as a

18-21 题 需要我们注意rank()和row_number()函数的用法。

  1. 统计各科成绩各分数段人数:课程编号,课程名称,[100-85],[85-70],[70-60],[60-0] 及所占百分比
select Course.Cname, a.* from course inner join (select CID,
sum(case when score <=100 and score > 85 then 1 else 0 end )/count(*) as range_100_85,
sum(case when score <=85 and score > 70 then 1 else 0 end )/count(*) as range_85_70,
sum(case when score <=70 and score > 60 then 1 else 0 end  )/count(*) as range_70_60,
sum(case when score <=60 then 1 else 0 end)/count(*) as not_pass from SC group by CID) as a
on course.CID = a.CID
  1. 查询各科成绩前三名的记录
select * from (select SID, CID, row_number() over(partition by CID order by score DESC) as rank_num 
from SC) as a where a.rank_num < 4
  1. 查询出只选修两门课程的学生学号和姓名
select Student.SID, Student.Sname from Student inner join 
(select SID, count(*) from SC group by SID having count(*) = 2) as a
on Student.SID = a.SID
  1. 查询男生、女生人数
select Ssex,count(*) from Student group by Ssex
  1. 查询名字中含有「风」字的学生信息
select * from Student where Sname like '%风%'
  1. 查询同名同姓学生名单,并统计同名人数
select * from 
(select Sname, count(*) as student_num from Student group by Sname) as a
where a.student_num > 1
  1. 查询 1990 年出生的学生名单
select * from Student where year(Sage) = 1990
  1. 查询每门课程的平均成绩,结果按平均成绩降序排列,平均成绩相同时,按课程编号升序排列
select CID,avg(score) as avg_score from SC group by CID order by avg_score DESC, CID ASC
  1. 查询平均成绩大于等于 85 的所有学生的学号、姓名和平均成绩
select Student.SID, Student.Sname, avg_score from Student inner join
(select SID, avg(score) as avg_score from SC group by SID having avg(score) >= 85) as a
on Student.SID = a.SID
  1. 查询课程名称为「数学」,且分数低于 60 的学生姓名和分数
select * from Student, SC, Course
where course.Cname = '数学' and SC.score < 60 and Student.SID = SC.SID and SC.CID = Course.CID 
  1. 查询所有学生的课程及分数情况(存在学生没成绩,没选课的情况)
select * from Student left join SC
on Student.SID = SC.SID
  1. 查询任何一门课程成绩都在 70 分以上的姓名、课程名称和分数
select Student.Sname, Course.Cname, SC.score from Student, Course, SC where Student.SID not in
(select Distinct SID from SC where SC.score < 70) and Student.SID = SC.SID and SC.CID = Course.CID
  1. 查询存在不及格的课程
select * from Course where CID in 
(select Distinct CID from SC where SC.score < 60)
  1. 查询课程编号为 01 且课程成绩在 80 分以上的学生的学号和姓名
select Student.SID, Student.Sname,a.CID, a.score from Student inner join
(select SID,score,CID from SC where CID = '01' and score >= 80) as a
on Student.SID = a.SID
  1. 求每门课程的学生人数
select CID, count(*) as student_num from SC group by CID 
  1. 成绩不重复,查询选修「张三」老师所授课程的学生中,成绩最高的学生信息及其成绩
select Student.*, a.score from Student inner join 
(select SID, score, row_number() over(partition by CID order by score DESC) as rank_num from SC where CID in (select CID from Teacher,Course where Tname = '张三' and Course.TID = Teacher.TID)) as a
on Student.SID = a.SID where rank_num = 1
  1. 成绩有重复的情况下,查询选修「张三」老师所授课程的学生中,成绩最高的学生信息及其成绩
select Student.*, a.score from Student inner join 
(select SID, score, rank() over(partition by CID order by score DESC) as rank_num from SC where CID in (select CID from Teacher,Course where Tname = '张三' and Course.TID = Teacher.TID)) as a
on Student.SID = a.SID where rank_num = 1
  1. 查询不同课程成绩相同的学生的学生编号、课程编号、学生成绩
select * from SC where exists(select * from SC as a where a.SID = SC.SID and a.CID <> SC.CID and a.score = SC.score)

考察Exists的用法。

  1. 按照出生日期来算,当前月日 > 出生年月的月日则,年龄减一
    直接手算的方法,不如知乎答主的简便。
select Student.*, case when month(now()) < month(Sage) | (month(now()) = month(Sage) and day(now()) < day(Sage)) then year(now()) -  year(Sage) - 1 else year(now()) -  year(Sage) end as age
from Student

下面是知乎上面的解答:

select student.SId as 学生编号,student.Sname  as  学生姓名,Sage, TIMESTAMPDIFF(YEAR,student.Sage,CURDATE()) as 学生年龄
from student

这里需要注意TIMESTAMPDIFF(interval, datetime1, datetime2),interval是year, month, day,表示间隔最后的单位而两个datetime1,和datetime2则是表示间隔。curdate() 内置函数返回的是当前的日期值。

  1. 查询本周过生日的学生
select * from Student where
week(curdate()) = week(DATE_ADD(Sage,INTERVAL year(curdate()) - year(Sage) YEAR))

使用到了DATE_ADD()这个函数,函数中的意思是值在Sage的日期上加上间隔的年份(注意这里不能使用timestampdiff()函数)。
43. 查询下周过生日的学生

select * from Student where
week(curdate()) = week(DATE_ADD(Sage,INTERVAL year(curdate()) - year(Sage) YEAR))-1

类似上一题。

  1. 查询本月过生日的学生
select * from Student where
month(curdate()) = month(Sage) 

相对于week这个就比较简单了。

  1. 查询下月过生日的学生
select * from Student where
month(curdate()) = month(Sage) 

你可能感兴趣的:(SQL)