本次笔记内容旨在为了提高自己平时编写SQL的能力,内容大都为自己的一些思考和网上的相关博客,本篇笔记中的所有案例均已通过编译。对于笔记中的错误,欢迎大家批评指正。注意:大部分代码使用的数据库为MySQL5.7.32,小部分代码使用的数据库为MySQL8.0.23。
目录
0 创建数据库,并导入练习数据
0.1 创建数据库
0.2 插入数据
1 查询“01”课程和“02”课程的成绩
1.1 查询"01"课程比"02"课程成绩高的学生的信息及课程分数
1.2 查询同时存在" 01 "课程和"02"课程成绩的学生的信息及课程分数
1.3 查询存在"01"课程但可能不存在"02"课程的情况(不存在时显示为null )
1.4 查询不存在"01"课程但存在"02"课程的情况
2 查询平均成绩大于等于 60 分的同学的学生编号、学生姓名和平均成绩
3 查询在成绩表存在成绩的学生信息
4 查询所有同学的学生编号、学生姓名、选课总数、所有课程的总成绩(没成绩的显示为null)
4.1 查有成绩的学生信息
5 查询「李」姓老师的数量
6 查询学过「王珊」老师授课的同学的信息
7 查询没有学全所有课程的同学的信息
8 查询至少有一门课与学号为"01"的同学所学相同的同学的信息
9 查询和"01"号的同学学习的课程完全相同的其他同学的信息
10 查询没学过张衡老师讲授的任一门课程的学生姓名
11 查询两门及其以上不及格课程的学生的学号,姓名及其平均成绩
12 检索01课程分数小于 60,按分数降序排列的学生信息
13 按平均成绩从高到低显示所有学生的所有课程的成绩以及平均成绩
14 查询各科成绩最高分、最低分和平均分
15 成绩进行排序,并显示排名
15.1 按各科成绩进行排序,并显示排名(无并列排名)
15.2 按各科成绩进行排序,并显示排名,score重复时合并名次
15.3 按各科成绩进行排序,并显示排名, score 重复时保留名次空缺
15.4 按成绩进行排序,并显示排名, Score重复时继续排序
15.5 按成绩进行排序,并显示排名, Score重复时合并名次
16 査询学生的总成绩,并进行排名
16.1 总分重复时不保留名次空缺
16.2 总分重复时保留名次空缺
17 查询各科成绩前三名的记录
18 查询每门课程的学生数
19 查询出只选修两门课程的学生学号和姓名
20 查询男女生人数
21 查询名字中含有枫字的学生信息
22 查询同名同性学生名单,并统计同名同性人数
23 查询1990年出生的学生名单
24 查询每门课程的平均成绩,结果按平均成绩降序排列,平均成绩相同时,按课程编号升序排列
25 查询平均成绩大于等于 85 的所有学生的学号、姓名和平均成绩
26 查询课程名称为数学,且分数低于60的学生姓名和分数
27 查询所有学生的课程及分数情况(存在学生没成绩,没选课的情况)
28 查询任何一门课程成绩在70分以上的姓名、课程名称和分数
29 查询有不及格成绩的课程,以及该课程对应的不及格学生数量
30 查询课程编号为01且课程成绩在80分及其以上的学生的学号和姓名
31 假设成绩不重复,查询选修「张衡」老师所授课程的学生中,成绩最高的学生信息及其成绩
32 成绩有重复的情况下,查询选修「张三」老师所授课程的学生中,成绩最高的学生信息及其成绩
33 查询不同课程成绩相同的学生的学生编号、课程编号、学生成绩
34 查询每门科目成绩最好的前两名
35 查询选修了全部课程的学生信息
36 查询各学生的年龄,只按年份来算
37 按照出生日期来算年龄,当前月日<出生年月的月日,则年龄减一
38 查询本周过生日的学生
39 查询下周过生日的学生
40 查询本月过生日的学生
41 查询下月过生日的学生
42 查询学过“01”学生并且也学过“11”学生课程的同学的信息
43 查询所有课程成绩大于60的同学的信息
参考资料
(1)创建学生表
create table student (
sid varchar(10),
sname varchar(10),
sbirth datetime,
sgender char(1)
)engine myisam charset utf8;
(2)创建课程表
create table course(
cid varchar(10),
cname varchar(10),
tid varchar(10)
)engine myisam charset utf8;
(3)创建教师表
create table teacher(
tid varchar(10),
tname varchar(10)
)engine myisam charset utf8;
(4)创建成绩表
create table grade(
sid varchar(10),
cid varchar(10),
score decimal(4,1)
)engine myisam charset utf8;
(1) 学生表插入数据
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','女');
(2)课程表插入数据
insert into course values
('01','语文','02'),
('02','数学','01'),
('03','英语','03'),
('04','政治','04');
(3)教师表插入数据
insert into teacher values
('01','张衡'),
('02','李文'),
('03','吴迪'),
('04','王珊');
(4)成绩表插入数据
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','02',82),
('10','01',88),
('10','02',90),
('11','01',80),
('13','01',59),
('13','02',79),
('13','04',81);
select y.*, x.cid1, x.score1, x.cid2, x.score2
from (
select a.sid, a.cid as cid1, a.score score1, b.cid cid2, b.score score2
from grade a join grade b on b.sid=a.sid
where a.cid='01' and b.cid='02' and a.score>b.score
) x left join student y on y.sid=x.sid;
select y.*, x.cid1, x.score1, x.cid2, x.score2
from(
select a.sid, a.cid cid1, a.score score1, b.cid cid2, b.score score2
from grade a join grade b on b.sid=a.sid
where a.cid='01' and b.cid='02'
) x left join student y on y.sid=x.sid;
-- 方法1
select *
from grade a left join grade b
on a.sid=b.sid and b.cid='02'
where a.cid='01';
-- 方法2
select *
from (select * from grade where cid='01') a
left join(select * from grade where cid='02') b
on b.sid = a.sid;
-- 方法1
select * from grade where cid='02' and sid in(
select sid from student where sid not in(
select sid from grade where cid='01'
)
);
-- 方法2
select *
from (select sid, cid cid1, score score1 from grade where cid='01') a
right join (select sid, cid cid2, score score2 from grade where cid='02') b
on b.sid=a.sid
where cid1 is null;
select b.sname, a.*
from(
select sid,avg(score) avg_score
from grade group by sid
having avg(score)>=60
) a left join student b on b.sid=a.sid;
-- 方法1
select * from student where sid in(
select distinct sid
from grade where score is not null
);
-- 方法2
select b.*
from(
select sid from grade group by sid
) a left join student b on b.sid=a.sid;
-- 方法1
select a.sid, a.sname, count(b.cid) cnt, sum(b.score) sum_score
from student a left join grade b on b.sid=a.sid
group by a.sid, a.sname;
-- 方法2
select a.sid, a.sname, b.cnt, b.sum_score
from student a
left join(
select sid, count(cid) cnt, sum(score) sum_score
from grade group by sid
) b on b.sid=a.sid;
select * from A where id in (select id from B);
select * from A where exists (select 1 from B where A.id=B.id);
对于以上两种情况,in是在内存里遍历比较,而exists需要查询数据库,所以当B表数据量较大时,exists效率优于in。
-- 方法1
select * from student
where sid in (select sid from grade);
-- 方法2
select * from student a
where exists (
select sid from grade b
where b.sid=a.sid
);
select count(1) from teacher where tname like '李%';
select * from student where sid in(
select sid
from grade a
left join course b on b.cid=a.cid
left join teacher c on c.tid=b.tid
where c.tname='王珊'
);
select * from student where sid not in(
select sid from grade group by sid
having count(cid) = (select count(1) from course)
);
select * from student where sid in(
select sid from grade where cid in(
select cid from grade where sid='01'
)
) and sid <> '01';
提示:
①没有学习01号没有学习的课程
②学习的课程数量和01号同学一样
select * from student a
join(
select sid from grade
where sid <> '01' and sid not in(
select sid from grade where cid not in(
select cid from grade where sid='01'
)
) group by sid having count(cid) =(
select count(1) from grade where sid='01'
)
) b on b.sid=a.sid;
select * from student where sid not in(
select sid from grade where cid in(
select b.cid
from grade a
left join course b on b.cid=a.cid
left join teacher c on c.tid=b.tid
where c.tname='张衡'
)
);
-- 方法1
select a.sid, b.sname, a.avg_score
from (
select sid, avg(score) avg_score
from grade where sid in(
select sid
from grade
where score<60
group by sid
having count(cid)>=2
) group by sid
) a left join student b on b.sid=a.sid;
-- 方法2
select a.sid, b.sname, a.avg_score
from (
select sid, avg(score) avg_score
from grade group by sid
having sum(score<60)>1
) a left join student b on b.sid=a.sid;
思路:先找出01课程分数小于 60的学生成绩信息,再以此条件从grade表左连接student表的结果中筛选出对应的学生信息,并将最终结果按分数降序排列。
select b.*, a.cid, a.score
from (
select * from grade
where cid='01' and score<60
) a left join student b on b.sid=a.sid
order by a.score desc;
select a.sid, avg(a.score) `平均成绩`,
max(if(b.cname='政治', a.score, null)) `政治`,
max(if(b.cname='数学', a.score, null)) `数学`,
max(if(b.cname='语文', a.score, null)) `语文`,
max(if(b.cname='英语', a.score, null)) `英语`
from grade a inner join course b on b.cid=a.cid
group by a.sid
order by `平均成绩` desc;
以如下形式显示:课程ID,课程name,最高分,最低分,平均分,及格率,中等率,优良率,优秀率。
要求输出课程号和选修人数,查询结果按人数降序排列,若人数相同,按课程号升序排列
其中,及格为>=60,中等为[70-80),优良为[80-90),优秀为>=90
select
a.cid 课程ID,
b.cname 课程Name,
count(1) 选修人数,
max(a.score) 最高分,
min(a.score) 最低分,
avg(a.score) 平均分,
concat(round(sum(90<=a.score)/count(1)*100,2),'%') 优秀率,
concat(round(sum(80<=a.score && a.score<90)/count(1)*100,2),'%') 优良率,
concat(round(sum(70<=a.score && a.score<80)/count(1)*100,2),'%') 中等率,
concat(round(sum(60<=a.score)/count(1)*100,2),'%') 及格率
from grade a join course b on b.cid=a.cid
group by a.cid, b.cname
order by count(1) desc, a.cid asc;
注意:由于在MySQL5.7.32中不支持开窗函数,因此15.1-15.3无法运行在MySQL5.7.32上,但是这些能够在MySQL8.0.23上运行。
select
sid,
cid,
score,
row_number() over(partition by cid order by score desc) as ranking
from grade;
select
sid,
cid,
score,
dense_rank() over(partition by cid order by score desc) as ranking
from grade;
select
sid,
cid,
score,
rank() over(partition by cid order by score desc) as ranking
from grade;
select
sid, cid, score,
@rn:=@rn+1 as rank
from grade, (select @rn:=0) tmp
order by score desc;
select
sid, cid, score,
case when score=@sco then @rn
when @sco:=score then @rn:=@rn+1
end rank
from grade, (select @rn:=0, @sco:=null) tmp
order by score desc;
-- 方法1,mysql5.7.32
select a.*,
@rn:=if(sum_score=@sco, @rn, @rn+1) rank,
@sco:=sum_score
from (
select sid, sum(score) sum_score
from grade group by sid
) a, (select @sco:=null, @rn:=0) b
order by sum_score desc;
-- 方法2,mysql8.0.23
select
sid,
sum_score,
dense_rank() over(order by sum_score desc) ranking
from(
select sid, sum(score) sum_score
from grade group by sid
) a;
-- 方法1,mysql5.7.32
select sid, sum_score,
@currank:=if(sum_score=@sco, @currank, @incrank) as rank,
@incrank:=@incrank+1,
@sco:=sum_score
from (select sid, sum(score) as sum_score from grade group by sid
) c, (select @currank:=0, @sco:=null, @incrank:=1) r
order by sum_score desc;
-- 方法2,mysql8.0.23
select
sid,
sum_score,
rank() over(order by sum_score desc) ranking
from(
select sid, sum(score) sum_score
from grade group by sid
) a;
思路:计算比自己分数大的记录有几条,如果小于3 就select,因为对前三名来说不会有3个及以上的分数比自己大了,最后再对所有select到的结果按照分数和课程编号排名即可。
-- 方法1,mysql5.7.32
select * from grade a
where (
select count(1) from grade b
where a.cid=b.cid and a.score
select cid, count(1) student_cnt
from grade group by cid;
select a.sid, b.sname
from (
select sid from grade group by sid
having count(cid)=2
) a left join student b on b.sid=a.sid;
select sgender, count(1) as cnt
from student group by sgender;
select * from student
where sname like '%枫%';
select sname, sgender, count(sid) cnt
from student group by sname, sgender
having cnt>1;
select * from student
where year(sbirth)=1990;
select cid, avg(score) avg_score
from grade group by cid
order by avg_score desc, cid asc;
select a.sid, b.sname, avg(a.score) avg_score
from grade a
left join student b on b.sid=a.sid
group by a.sid,b.sname having avg_score>=85;
select c.sname, b.cname, a.score
from grade a
left join course b on b.cid=a.cid
left join student c on c.sid=a.sid
where b.cname='数学' and a.score<60;
select a.*, c.cname, b.score
from student a
left join grade b on b.sid=a.sid
left join course c on c.cid=b.cid;
select a.sid, b.sname, c.cname, a.score
from grade a
left join student b on b.sid=a.sid
left join course c on c.cid=a.cid
where a.score>70;
[扩展]
查询有且仅有一门课程成绩在 70 分以上的学生的学号、姓名、课程名称和分数
select a.sid, b.sname, c.cname, a.score
from grade a
left join student b on b.sid=a.sid
left join course c on c.cid=a.cid
where a.sid in(
select sid from grade
group by sid having sum(score>70)=1
) and a.score>70;
select a.cid, b.cname, sum(a.score<60) not_pass_num
from grade a left join course b on b.cid=a.cid
group by a.cid, b.cname having min(a.score) < 60;
select * from student
where sid in(
select sid
from grade
where cid='01' and score>80
);
select a.sid, d.sname, b.cname, a.score
from grade a
left join course b on b.cid=a.cid
left join teacher c on c.tid=b.tid
left join student d on d.sid=a.sid
where c.tname='张衡'
order by a.score desc limit 1;
-- 方法1
select a.sid,d.sname,b.cname,a.score
from grade a
left join course b on b.cid=a.cid
left join teacher c on c.tid=b.tid
left join student d on d.sid=a.sid
where c.tname='张衡' and score=(
select max(score)
from grade a
left join course b on b.cid=a.cid
left join teacher c on c.tid=b.tid
where c.tname='张衡'
);
-- 方法2
select sid,sname,cname,score
from(
select
x.*,
@cur_rank:=if(x.score=@cur_sco,@cur_rank,@cur_rank+1) rank,
@cur_sco:=x.score
from(
select d.sid, d.sname, a.cid, b.cname, a.score
from grade a
left join course b on b.cid=a.cid
left join teacher c on c.tid=b.tid
left join student d on d.sid=a.sid
where c.tname='张衡'
order by a.score desc
) x, (select @cur_sco:=null, @cur_rank:=0) y
) tmp where rank=1;
-- 方法3,MySQL8.0.23
select sid,sname,cname,score
from(
select a.sid,d.sname,b.cname,a.score,
rank() over(order by score desc) ranking
from grade a
left join course b on b.cid=a.cid
left join teacher c on c.tid=b.tid
left join student d on d.sid=a.sid
where c.tname='张衡'
) a where a.ranking=1;
select a.sid,a.cid,a.score
from grade a, grade b
where a.cid<>b.cid and a.score=b.score and a.sid=b.sid
group by a.sid, a.cid,a.score;
-- 方法1,MySQL5.7.32
select sid,cid,score,rank
from(
select x.*,
@cur_rank:=if(x.cid=@cur_cid, if(x.score=@cur_score, @cur_rank, @cur_rank+1), 1) rank,
@cur_cid:=x.cid,
@cur_score:=x.score
from grade x, (select @cur_cid:=null, @cur_score:=null, @cur_rank:=0) y
order by cid asc, score desc
) tmp where tmp.rank<3;
-- 方法2,MySQL8.0.23
select * from(
select *,
dense_rank() over(partition by cid order by score desc) ranking
from grade
) t where t.ranking <3;
select * from student where sid in(
select sid
from grade group by sid
having count(cid)=(
select count(1) from course
)
);
select *, year(now())-year(sbirth) age
from student;
select *, timestampdiff(year, sbirth, now()) age
from student;
-- 假设今天日期为2020-12-22
select * from student
where week(sbirth)=week('2020-12-15');
-- 本题不是很准确,没有考虑1月1日必须为一年的第一周
-- 假设今天的日期为2020-12-15
select * from student
where week(sbirth)=week('2020-12-15')+1;
-- 假设今天的日期为2020-12-15
select * from student
where month(sbirth)=month('2020-12-15');
-- 假设今天的日期为2020-12-15
select * from student
where month(sbirth)=if(
month('2020-12-15')=12,1,month('2020-12-15')+1
);
select * from student where sid in(
select sid from grade where cid in(
select a.cid from grade a
inner join grade b on b.cid=a.cid
where a.sid='01' and b.sid='11'
)
);
select * from student where sid in(
select sid
from grade group by sid
having min(score)>60
);
【1】https://www.jianshu.com/p/476b52ee4f1b
【2】https://www.it610.com/article/1290585177927524352.htm
【3】https://blog.csdn.net/qq_41080850/article/details/84593860
【4】https://blog.csdn.net/hundan_520520/article/details/54881208?utm_medium=distribute.wap_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.wap_blog_relevant_no_pic&depth_1-utm_source=distribute.wap_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.wap_blog_relevant_no_pic
【5】https://juejin.cn/post/6844904146508709901
【6】https://blog.csdn.net/youmianzhou/article/details/86562649?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522160031920419724839817031%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=160031920419724839817031&biz_id=0&utm_medium=distribute.wap_search_result.none-task-blog-2~all~sobaiduend~default-8-86562649.wap_ecpm_v3_pc_rank_v3&utm_term=mysql%E7%BB%83%E4%B9%A0%E9%A2%98&spm=1018.2118.3001.4187
【7】http://mp.weixin.qq.com/mp/homepage?__biz=MzU2MDg0MTY0NA==&hid=4&sn=63247e5e067222e9f601d37e385c0ae3&scene=18#wechat_redirect
【8】https://blog.csdn.net/d345389812/article/details/80683619