详解50道SQL练习题

数据表说明:

学生表student(sid,sname,sbirth,sgender)参数说明:

    sid:学生编号,sname:学生姓名,sbirth:学生出生日期,sgender:学生性别

课程表course(cid,cname,tid)参数说明:

    cid:课程编号,cname:课程名字,tid:授课老师编号

教师表teacher(tid,tname)参数说明:

    tid:教师编号,tname:教师姓名

成绩表grade(sid,cid,score)参数说明:

    sid:学生编号,cid:课程编号,score:课程成绩

创建上述各表,并向表中插入数据:

# 创建学生表:
create table student (
sid varchar(10),
sname varchar(10),
sbirth datetime,
sgender char(1)
)engine myisam charset utf8;

# 向学生表中插入数据:
insert into student values
('01','赵雷','1990-01-01','男'),
('02','钱枫','1990-12-21','男'),
('03','孙思邈','1990-12-16','男'),
('04','李四','1990-12-06','男'),
('05','周梅','1991-12-01','女'),
('06','吴兰','1992-01-01','女'),
('07','郑珠','1989-01-01','女'),
('09','张三','2017-12-20','女'),
('10','李四','2017-12-16','女'),
('11','李四','2012-06-06','女'),
('12','赵雷','2013-06-13','男'),
('13','孙琦','2014-06-01','女');


# 创建科目表:
create table course(
cid varchar(10),
cname varchar(10),
tid varchar(10)
)engine myisam charset utf8;

# 向科目表中插入数据:
insert into course values
('01','语文','02'),
('02','数学','01'),
('03','英语','03'),
('04','政治','04');


# 创建教师表:
create table teacher(
tid varchar(10),
tname varchar(10)
)engine myisam charset utf8;

# 向教师表中插入数据:
insert into teacher values
('01','张衡'),
('02','李文'),
('03','吴迪'),
('04','王珊');


# 创建成绩表:
create table grade(
sid varchar(10),
cid varchar(10),
score decimal(4,1)
)engine myisam charset utf8;

# 向成绩表中插入数据:
insert into grade values
('01','01',80),
('01','02',90),
('02','01',70),
('02','02',60),
('02','03',80),
('02','04',85),
('03','01',80),
('03','02',80),
('03','03',80),
('03','04',61),
('04','01',75),
('04','02',30),
('04','03',20),
('04','04',45),
('05','01',76),
('05','02',87),
('05','03',99),
('05','04',75),
('06','01',31),
('06','02',65),
('06','03',34),
('07','02',89),
('07','03',98),
('09','03',82),
('10','01',88),
('10','02',90),
('13','01',59),
('13','02',79),
('13','04',81);

注:以下习题解答均测试于MySQL环境下。

习题及其解答:

1.1 查询01课程比02课程成绩高的学生信息及课程分数:

思路:先找出同时选修了01课程和02课程的学生编号和课程对应的成绩,然后筛选出01课程成绩比02课程成绩高的学生编号和课程对应的成绩,最后将筛选结果与学生表student左连接,再次筛选出相应学生的信息。

代码:

select * from 
(select * from (select sid,score as score1 from grade where cid = '01') as t1
inner join 
(select sid,score as score2 from grade where cid = '02') as t2 
using(sid) 
where t1.score1 > t2.score2
) as temp left join student using(sid);

1.2查询同时选修了01课程和02课程的学生的课程分数:

思路:选修了01课程的学生与选修了02课程的学生的交集即是同时选修了01、02两门课程的学生。

代码:

select * from
(select sid,score as score1 from grade where cid = '01') as t1
inner join 
(select sid,score as score2 from grade where cid = '02') as t2 
using(sid) ;

使用left join同样能达成与上述代码同样的效果:

select * from
(select sid,score as score1 from grade where cid = '01') as t1
left join 
(select sid,score as score2 from grade where cid = '02') as t2 
using(sid) 
where t2.score is not null;

1.3查询存在01课程但可能不存在02课程的情况(不存在时显示为 null )

思路:此题为典型的左连接题,可以将选修了01课程的学生的成绩信息左连接于选修了02课程的学生的成绩信息。由于采用左连接,所以02课程不存在时自动补充null值。

代码:

select * from 
(select * from grade where cid = '01') as t1 
left join 
(select * from grade where cid = '02') as t2 
using(sid);

1.4 查询不存在01课程但存在02课程的情况:

思路:先找出没有选修01课程的学生的成绩信息,再进一步筛选出同时选修了02课程的学生的成绩信息。

代码:

select * from grade 
where sid not in (select sid from grade where cid = '01') 
having sid in (select sid from grade where cid = '02');

2.查询平均成绩大于等于 60 分的同学的学生编号和学生姓名和平均成绩:

思路:先找出平均成绩大于等于60分的同学的学生编号和平均成绩,再将结果与student表左连接以找出对应的学生姓名。

代码:

select sid,sname,avg_score from 
(select sid,avg(score) as avg_score from grade group by sid having avg_score > 60) as t1 
left join student using(sid);

3.查询在 grade表中存在成绩的学生信息:

思路:用grade表中的学生编号对student中的学生信息进行筛选。

方法一:

 select * from student where sid in (select sid from grade);

方法二:

select * from student where exists(select * from grade where grade.sid = student.sid);

4.查询所有同学的学生学号、姓名、选课总数、所有课程的总成绩(没成绩的显示为 null ):

思路:将学生表student与成绩表grade左连接,根据已知条件对两表左连接结果进行聚合计算。

select sid,sname,count(*) as cour_num,sum(score) as total_score from
student left join grade using(sid)
group by sid;

5.查询李姓老师的数量:

思路:使用通配符%进行模糊查询。

代码:

select count(*) as num_Li from teacher where tname like '李%';

6.查询学过张衡老师授课的同学的信息:

思路:与3类似,但此题涉及到了四张表。可以先由授课老师姓名找出对应的讲授课程编号,然后以此课程编号为筛选条件,从grade表中筛选出选修了张三老师讲授课程的学生学号,再以此学生学号为筛选条件,从student表中筛选出对应的学生信息。

代码:

select * from student where sid in 
(select sid from grade where cid = (select cid from course where tid = (select tid from 
teacher where tname = '张衡')));

7.查询没有学全所有课程的同学的信息:

思路:从grade表中找出没有学全所有课程的学生的学号,再根据此学号从student表中筛选出相应学生的信息。

代码:

select * from student where sid not in (select sid from grade group by sid having count(*) = 4);

8.查询至少有一门课与学号为01 的同学所学相同的同学的信息:

思路:从grade表中先找出学号为01的学生选修的课程的编号,以选出来的课程编号作为条件,再从grade表中筛选出至少有一门课与学号为01的同学所学相同的同学的学生学号,最后根据筛选出来的学生编号从student表中再次筛选出相应学生的信息。

代码:

select * from student where sid in 
(select distinct sid from grade where cid in 
(select cid from grade where sid = '01') 
and sid != '01');

9.查询和01号的同学学习的课程 完全相同的其他同学的信息:

此题很难,所以单独写一篇博客对其进行详解,链接:https://blog.csdn.net/qq_41080850/article/details/84648897

10.查询没学过张衡老师讲授的任一门课程的学生姓名:

思路:先找出选修了张衡老师讲授的课的学生编号,剩下的则是没选修过张衡老师讲授的任一门课的学生编号,再以剩下学生的编号为筛选条件,从student表中筛选出对应学生的姓名。

代码:

select sname from student where sid not in 
(select sid from grade where cid in 
(select cid from course where tid = (select tid from teacher where tname = '张衡')));

11.查询两门及其以上不及格课程的学生的学号,姓名及其平均成绩:

思路:先找出有两门及其以上不及格课程的学生的学号和平均成绩,再将此结果与student表左连接找出对应学生编号的学生姓名。

代码:

select sid,sname,avg_sc from 
(select sid,avg(score) as avg_sc from grade group by sid having sum(score<60) > 1) as temp 
left join 
student 
using(sid);

12.检索01课程分数小于 60,按分数降序排列的学生信息:

思路:先找出01课程分数小于 60的学生学号,再以此为条件从grade表左连接student表的结果中筛选出对应的学生信息,并将最终结果按分数降序排列。

代码:

select student.*,cid,score from grade left join student using(sid) 
where sid in (select sid from grade where cid = '01' and score<60) 
order by score desc;

13.按平均成绩从高到低显示所有学生的所有课程的成绩以及平均成绩:

思路:先找出有成绩学生的编号和平均成绩,然后将此结果与成绩表grade左连接,再从左连接结果中查询出所有有成绩学的各门课程成绩以及平均成绩,并将最终的结果按平均成绩降序排序。

代码:

select grade.*,avg_sc from 
(select sid,avg(score) as avg_sc from grade group by sid) as temp left join grade using(sid) 
order by avg_sc desc;

14.查询各科成绩最高分、最低分和平均分:
以如下形式显示:课程 ID,课程 name,最高分,最低分,平均分,及格率,中等率,优良率,优秀率。
及格为>=60,中等为:70-80,优良为:80-90,优秀为:>=90。
要求输出课程编号和选修人数,查询结果按人数降序排列,若人数相同,按课程编号升序排列。

代码:

select cid,count(sid) as stu_num,max(score) as max_sc,min(score) as min_sc,
avg(score) as avg_sc,
concat(round((sum(score>=60)/count(*))*100,2),'%') as pass_rate,
concat(round((sum(score<80 && score>=70)/count(*))*100,2),'%') as medium_rate,
concat(round((sum(score>=80 && score<90)/count(*))*100,2),'%') as good_rate,
concat(round((sum(score>=90)/count(*))*100,2),'%') as excellent_rate 
from grade 
group by cid 
order by stu_num desc,cid asc;

15.1按各科成绩进行排序,并显示排名(无并列排名):

代码:

select grade.*,@curRank := @curRank+1 as rank from grade,(select @curRank := 0) as r 
order by score desc;

15.2 按各科成绩进行排序,并显示排名, score 重复时合并名次:

代码:

select grade.*, 
case 
when @prevRank = score then @curRank 
when (@prevRank := score) is not null then @curRank := @curRank + 1
end as rank
from grade, 
(select @curRank :=0, @prevRank := null) r
order by score desc;

15.3按各科成绩进行排序,并显示排名, score 重复时保留名次空缺:

代码:

select sid,cid,score,rank from
(select sid,cid,score,
@curRank := if(@prevRank = score, @curRank, @incRank) as rank, 
@incRank := @incRank + 1, 
@prevRank := score
from grade,(select @curRank :=0, @prevRank := null, @incRank := 1) as r 
order by score desc
) as temp;

实际上,在15.3的查询过程中用到的用户变量,也可以使用一条单独的SQL语句进行设置:

代码1:

set @curRank :=0, @prevRank := null, @incRank := 1;

然后再执行查询语句:

代码2:

select sid,cid,rank from
(select sid,cid,score,
@curRank := if(@prevRank = score, @curRank, @incRank) as rank, 
@incRank := @incRank + 1, 
@prevRank := score
from grade order by score desc) as temp;

16.1查询学生的总成绩,并进行排名,总分重复时合并名次:

代码:

select sid,total_score,
case 
when @prevRank = total_score  then @curRank 
when (@prevRank := total_score) is not null then @curRank := @curRank + 1
end as rank
from (select sid,sum(score) as total_score from grade group by sid ) as temp,
(select @curRank :=0, @prevRank := null) as r
order by total_score desc;

16.2查询学生的总成绩,并进行排名,总分重复时保留名次空缺:

代码:

select sid,total_score,rank from
(select sid,total_score,
@curRank := if(@prevRank = total_score, @curRank, @incRank) as rank, 
@incRank := @incRank + 1, 
@prevRank := total_score
from (select sid,sum(score) as total_score from grade group by sid) as t,
(select @curRank :=0, @prevRank := null, @incRank := 1) as r 
order by total_score desc
) as temp;

注:以上五道关于排名的查询题的思路基本相同,都是利用排序和自增变量。令人费解的是:在MySQL的语句执行顺序中,select的执行顺序明明要优先于order by,但上述五道题的查询结果却表明,select语句中的用户变量是在对score等字段排完序后,才开始增加的。

17.统计各科成绩各分数段人数:课程编号,课程名称,[100-85],(85-70],(70-60],(60-0] 及所占百分比:

此题与14题类似。

代码:

select cid,cname,
concat(round(sum(score<=100&&score>=85)/count(*)*100,2),'%') as '[100-85]',
concat(round(sum(score<85&&score>=70)/count(*)*100,2),'%') as '(85-70]',
concat(round(sum(score<70&&score>=60)/count(*)*100,2),'%') as '(70-60]',
concat(round(sum(score<60)/count(*)*100,2),'%') as '(60-0]' 
from grade left join course using(cid) 
group by cid;

18.查询各科成绩前三名的记录:

思路:先对各科的成绩按照降序排序,然后利用limit语句取出前三名的成绩信息,最后利用union语句将各科前三名的成绩信息合并起来。

代码:

(select cid,sid,score from grade where cid = '01' order by score desc limit 3)
union
(select cid,sid,score from grade where cid = '02' order by score desc limit 3)
union
(select cid,sid,score from grade where cid = '03' order by score desc limit 3)
union
(select cid,sid,score from grade where cid = '04' order by score desc limit 3);

19.查询每门课程被选修的学生数:

代码:

select cid,cname,count(sid) as stu_num from grade left join course using(cid) group by cid;

20.查询出只选修两门课程的学生学号和姓名:

思路:先找出只选修了两门课程的学生的学号,再以此为条件从student表中筛选出对应的学生学号和姓名。

代码:

select sid,sname from student where sid in (select sid from grade group by sid having count(cid) = 2);

21.查询男生、女生人数:

思路:按照性别对student表里的学生进行分组,然后分别统计男生与女生的人数。

代码:

select sgender,count(sid) as numbers from student group by sgender;

22.查询名字中含有枫字的学生信息:

此题为利用通配符%进行模糊查询,%可以匹配任意数量的任意字符。

代码:

select * from student where sname like '%枫%';

23.查询同名同性学生名单,并统计同名人数:

思路:先按照学生姓名和学生性别对对student表进行对字段分组,然后从分组结果中筛选出数量大于1的组。

代码:

select sname,sgender,count(sid) as num from student group by sname,sgender having num > 1;

24.查询 1990 年出生的学生名单:

思路:利用year函数从学生出生日期字段sbirth中提取出学生的出生年份,然后根据查询条件筛选出相应的学生名单。

代码:

 select sid,sname from student where year(sbirth)='1990';

25.查询每门课程的平均成绩,结果按平均成绩降序排列,平均成绩相同时,按课程编号升序排列:

代码:

 select cid,avg(score) as avg_sc from grade group by cid order by avg_sc desc,cid asc;

26.查询平均成绩大于等于 85 的所有学生的学号、姓名和平均成绩:

代码:

select sid,sname,avg(score) as avg_sc from grade left join student using(sid) 
group by sid having avg_sc >= 85;

27.查询课程名称为数学,且分数低于 60 的学生姓名和分数:

代码:

select sid,sname,score from grade left join student using(sid) 
where cid = (select cid from course where cname = '数学') having score < 60;

28.查询所有学生的课程及分数情况(存在学生没成绩,没选课的情况):

此题为典型的左连接题。

代码:

select sid,sname,cid,cname,score from student left join grade using(sid) left join course using(cid);

29.1查询至少一门课程成绩在 70 分以上的学生的学号、姓名、课程名称和分数:

思路:先找出所有课程成绩均不大于70分的学生的学号,剩下则都是至少有一门课的成绩在70分以上的学生。以剩余学生的学号为条件,从grade、student、course这三张表的左连接结果中筛选出相应学生的学号、姓名、课程名称和分数。

代码:

select sid,sname,cname,score from grade left join student using(sid) left join course using(cid) 
where sid not in (select sid from grade group by sid having max(score) <= 70);

29.2查询有且仅有一门课程成绩在 70 分以上的学生的学号、姓名、课程名称和分数:

思路:利用聚合函数sum统计成绩在70分以上的课程数。

代码:

select sid,sname,cname,score from grade left join student using(sid) left join course using(cid) 
group by sid having sum(score > 70) = 1;

30.查询有不及格学生的课程,并统计不及格学生的人数:

思路:利用聚合函数sum统计各科不及格学生的人数。

代码:

select cid,cname,sum(score<60) as not_pass_num from grade left join course using(cid) 
group by cid having min(score) < 60;

31.查询课程编号为 01 且课程成绩在 80 分以上的学生的学号和姓名:

代码:

select sid,sname from student where sid in (select sid from grade where cid = '01' and score > 80);

32.成绩有重复的情况下,查询选修张衡老师所授课程的学生中,成绩最高的学生信息及其成绩:
思路:先找出张衡老师所受课程的编号和所授课程中的最高分,然后以课程编号和最高成绩为条件,从grade表和student表的左连接结果中筛选出对应的学生信息。
代码:

select student.*,score from grade left join student using(sid)
where cid = (select cid from course where tid = (select tid from teacher where tname = '张衡'))
and
score = (select max(score) from grade where cid = (select cid from course where tid = 
(select tid from teacher where tname = '张衡')));

33.成绩不重复,查询选修张衡老师所授课程的学生中,成绩最高的学生信息及其成绩:

方法一:第31题中的思路同样适用于本题。

代码:

select student.*,score from grade left join student using(sid)
where cid = (select cid from course where tid = (select tid from teacher where tname = '张衡'))
and
score = (select max(score) from grade where cid = (select cid from course where tid = 
(select tid from teacher where tname = '张衡')));

方法二:先查询出按照score降序排序后的张衡老师所授课的成绩信息,然后取排序后的成绩信息中的第一条与student表左连接。

代码:

select student.*,score from 
(select sid,score from grade where cid = (select cid from course where tid = (select tid 
from teacher where tname = '张衡')) order by score desc limit 1) as temp 
left join 
student 
using(sid);

34.统计每门课程的学生选修人数(超过 5 人的课程才统计):

思路:利用count函数分组计数。

select cid,cname,count(sid) as stu_num from grade left join course using(cid)
group by cid having stu_num > 5;

35.查询不同课程成绩相同的学生的学号、课程编号、成绩
原题如此。我的理解是要查询某一类学生,这类学生在参加了考试的各个课程中获得的成绩相同。
思路:采用自连接,以课程编号不同且成绩相同为条件,从自连接结果中筛选出对应学生的信息,并去除重复值。
代码:

select distinct g1.* from grade as g1 left join grade as g2 using(sid)
where g1.score = g2.score and g1.cid <> g2.cid;

36.检索至少选修两门课程的学生信息:
思路:利用count函数统计不同学生选修的不同课程数。
代码:

select student.* from grade left join student using(sid) group by sid having count(cid) > 1;

37.查询选修了全部课程的学生信息:
思路:先统计出所有不同课程的数量,在以此为条件筛选出相应的学生信息。
代码:

select student.* from grade left join student using(sid) 
group by sid having count(cid) = (select count(distinct cid) from course);

38.查询各学生的年龄,只按年份来算:
思路:利用year函数分别提取出当前日期和出生日期的年份,将两者之差作为年龄。
代码:

select student.*,year(now())-year(sbirth) as age from student;

39.查询各学生的年龄,按照出生日期来算,若当前月日小于出生年月的月,则年龄减一:

思路:利用timestampdiff函数。

代码:

select student.*,timestampdiff(year,sbirth, curdate()) as age from student;

40.查询本周过生日的学生:
思路:利用weekofyear函数分别将当前日期与出生日期转化为该年内第几周中的一天,并将两个转化值是否相等作为筛选条件。
代码:

select * from student where weekofyear(sbirth) = weekofyear(now());

41.查询下周过生日的学生:
此题与第40题类似。
代码:

select * from student where weekofyear(sbirth) = weekofyear(now())+1;

42.查询本月过生日的学生:

思路:利用month函数分别提取出当前日期和出生日期的年份,并以二者是否相等为筛选条件。

代码:

select * from student where month(sbirth)=month(now());

43.查询下月过生日的学生:
思路:当本月是1至11月中的某一个月时,下月月份等于本月月份加一;当本月是12月时,下月月份为1。
代码:

select * from student where month(sbirth)=
case month(now())
when month(now()) between 1 and 11 then month(now())+1
when 12 then 1
end;

参考:

https://www.jianshu.com/p/bb1b72a1623e

https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html

PS:本文为博主原创文章,转载请注明出处。

猜你喜欢

转载自blog.csdn.net/qq_41080850/article/details/84593860