-- 1.1 查询"01"课程比"02"课程成绩高的学生的信息及课程分数
SELECT student.*,t1.cid,t1.cscore,t2.cid,t2.cscore
FROM student
INNER join 
(select * from score where cid=01) as t1
on student.Sid=t1.sid
INNER join 
(select * from score where cid=02) as t2
on t2.sid=t1.sid
where t1.cscore>t2.cscore;

-- 1.2 查询同时上过"01"课程和"02"课程的学生
-- 方法一,内连接
SELECT student.*
FROM student
INNER join 
(select * from score where cid=01) as t1
on student.Sid=t1.sid
INNER join 
(select * from score where cid=02) as t2
on t2.sid=t1.sid;

-- 方法二,where 直接筛选
select * from student
where sid in (select sid from score where cid=01)
and sid in (select sid from score where cid=02);

-- 1.3 查询上过"01"课程但可能没上过"02"课程的学生 (这种情况显示为 null)
SELECT student.*,t1.*,t2.*
FROM student
INNER join 
(select * from score where cid=01) as t1
on student.Sid=t1.sid
left join 
(select * from score where cid=02) as t2
on t2.sid=t1.sid
where t2.cscore is null;

-- 1.4 查询没上过"01"课程,只上过"02"课程的学生
SELECT student.*,t1.*,t2.*
FROM student
INNER join 
(select * from score where cid=02) as t1
on student.Sid=t1.sid
left join 
(select * from score where cid=01) as t2
on t2.sid=t1.sid
where t2.cscore is null;

思考:以上四题,运用表连接的方式,内连接inner join,左连接left join。且由成绩表进行筛选后生成两个表(只有课程1的表和只有课程2的表),得出选两门课的学生,课程1分数>课程2分数的学生,只选课程1的学生,只选课程2的学生。要学会一表生2表,通过连接将一维表转二维表。

-- 2. 查询平均成绩大于等于 60 分的同学的学生编号、学生姓名和平均成绩
-- 方法一,左连接
select student.sid,student.sname,avg(score.cscore)
from student
left join score
on student.sid=score.sid
GROUP BY student.sid
having avg(score.cscore)>=60;
-- 方法二,子查询,leftjoin会有空值产生
select student.sid,student.sname,t1.平均分
from student
left join (select sid,cid,avg(cscore) as "平均分"
from score group by sid having avg(cscore)>=60) t1
on student.sid=t1.sid;

思考:每个学生--group by,分组后筛选用having。

-- 3. 查询在 SC 表存在成绩的学生信息
-- 方法一,左连接
select student.*,score.*
from student
left join score
on student.sid=score.sid
where score.cscore is not null;

-- 方法二,子查询
select * from student
where sid in (select sid from score where cscore is not null)

思考:左连接和子查询皆可

-- 4. 查询所有同学的学生编号、学生姓名、选课总数、所有课程的总成绩(没成绩的显示为 null)
select student.sid,student.sname,count(score.cscore) as 选课数,sum(score.cscore) as 总成绩
from student
left join score
on student.sid=score.sid
group by student.sid;

思考:左连接,有学生没成绩;group by分组聚合函数

-- 5. 查询「李」姓老师的数量
select count(tid)
from teacher
where tname like "李%";

思考:like运算符,与%一起使用,模糊查询

-- 6. 查询学过张三老师授课的同学的信息
select student.*
from teacher,course,score,student
where teacher.tname='张三'
and teacher.tid=course.tid
and course.tid=score.cid
and score.sid=student.sid;

思考:四表连接,首先得熟悉四表之间的联系!

-- 7. 查询没有学全所有课程的同学的信息(!!)先反向思维
select * from student
where student.sid not in(
select sid
from score
group by sid
having count(score.cid)=(select count(course.cid) from course));
#解法1:反向思考
select * from student where student.sid not in (
    select score.sid from score 
    group by score.sid 
    having count(score.cid) = (select count(course.cid) from course)
    );
#解法2:表连接方法1
select * from student
left join score
on student.sid = score.sid
group by score.sid
having count(score.cid) <> (select count(course.cid) from course);

#解法2:表连接方法2
select student.sid, count(score.cid) from student
inner join score
where student.sid = score.sid
group by student.sid
having count(score.cid) <> (select count(course.cid) from course);

思考:反向思维-not in 全学了,判断全学用所学课程的数量与实际开课的总数相比较

-- 8. 查询至少有一门课与学号为"01"的同学所学相同的同学的信息
-- 方法一:左连接&子查询&group by
select student.* from student
left join score
on student.sid=score.sid
where score.cid in (select score.cid from score where score.sid='01')
and score.sid <> 01
group by student.Sid;
-- 方法一:多个子查询
select * from student where student.sid in
    (select score.sid from score where score.sid <> 01 and score.cid in
        (select score.cid from score where score.sid = 01));

思考:其他同学,要排除01同学,

-- 9. 查询和"01"号的同学学习的课程完全相同的其他同学的信息
create table 选课 as select sid,GROUP_CONCAT(cid order by cid SEPARATOR '-') as 选课情况
from score group by sid;
-- 方法一,直接新建新表进行查询
select * from student where sid in 
(select sid from 选课
where 选课情况=(select 选课情况 from 选课 where sid=01)
and sid <> 01) 
-- 方法二,两表连接查询
select t1.*, sname from (
    (select sid, group_concat(cid order by cid SEPARATOR '-') as 选课情况
        from score
    group by sid) as t1
join
    (select sid, group_concat(cid order by cid SEPARATOR '-') as 选课情况 
        from score
    where sid = 01
    group by sid) as t2
on t1.选课情况 = t2.选课情况
join student on t1.sid = student.sid and t1.sid <> 01);

思考:选课完全相同的同学,group_concat(字段名 order by cid separator '-')

-- 10. 查询没学过"张三"老师讲授的任一门课程的学生姓名
-- 方法一,反向思维,左连接&子查询&group by,
-- 因为表连接有个坏处是,每个学生都有好多条记录,无法直接判断谁没学
select * from student where student.Sname not in (select student.Sname 
from teacher,course,score,student
where teacher.tid=course.tid
and course.cid=score.cid
and score.sid=student.Sid
and teacher.tname = '张三'
group by student.sid)
-- 方法二,多个子查询
select * from student where student.sid not in
    (select score.sid from score where score.cid in
        (select course.cid from course where 
            course.tid = (select teacher.tid from teacher where tname = '张三')));
-- 11. 查询两门及其以上不及格课程的同学的学号,姓名及其平均成绩
-- 方法一,用左连接+聚合函数筛选
select student.sid,student.Sname,avg(score.cscore)
from student
left join score
on student.sid=score.sid
where cscore < 60 
group by score.sid
having count(cid) >= 2;

-- 方法二,用where内连接
select student.sid,student.Sname,avg(score.cscore)
from student,score
where student.sid=score.sid
and cscore < 60 
group by score.sid
having count(cid) >= 2;
-- 12. 检索"01"课程分数小于 60,按分数降序排列的学生信息
select * from student
left join score
on student.sid=score.sid
where score.cscore < 60
and score.cid = 01
order by score.cscore desc

select student.sname, cscore from score 
join student 
on score.sid = student.sid
where cid = (select cid from course where cid = '01') and cscore < 60;
-- 13. 按平均成绩从高到低显示所有学生的所有课程的成绩以及平均成绩
-- 错误,所有课程成绩,不是总和
select student.*,sum(score.cscore) as '课程总分',avg(score.cscore) as '平均成绩'
from student
left join score
on student.sid=score.sid
GROUP BY student.sid
order by 平均成绩 desc
-- 错误,显示不了每个学生的每门成绩
select student.*,score.*,avg(score.cscore) as '平均成绩'
from student
left join score
on student.sid=score.sid
GROUP BY student.sid
order by 平均成绩 desc
-- 先求出平均成绩,然后在表连接为了显示所有课程成绩,要习惯用表自交
select t1.sid, t1.cscore, 平均成绩 from score as t1
left join 
    (select sid, avg(cscore) as 平均成绩 from score
    group by sid) as t2
on t1.sid = t2.sid
order by 平均成绩 desc;
-- 14. 查询各科成绩最高分、最低分和平均分,以如下形式显示:
-- 课程 ID,课程 name,最高分,最低分,平均分,及格率,中等率,优良率,优秀率
-- (及格为>=60,中等为:70-80,优良为:80-90,优秀为:>=90)
-- 要求输出课程号和选修人数,查询结果按人数降序排列,若人数相同,按课程号升序排列

select course.cid,course.cname,
count(score.sid) as 选修人数,
max(score.cscore) as 最高分,
min(score.cscore) as 最低分,
avg(score.cscore) as 平均分,
sum(case when score.cscore>=60 then 1 else 0 end)/count(score.sid) as 及格率,
sum(case when score.cscore>=70 and score.cscore<80 then 1 else 0 end)/count(score.sid) as 中等率,
sum(case when score.cscore>=80 and score.cscore<90 then 1 else 0 end)/count(score.sid) as 优良率,
sum(case when score.cscore>=90 then 1 else 0 end)/count(score.sid) as '优秀率'
from course
left join score
on course.cid=score.cid
group by course.cid
order by 选修人数 desc,course.cid;
-- 15.1 按各科成绩进行排序,并显示排名, 成绩重复时保留名次空缺
select *,
rank() over (partition by cid order by cscore desc) as 排名
from score;

-- 15.2 按各科成绩进行排序,并显示排名, 成绩重复时合并名次
select *,
dense_rank() over (partition by cid order by cscore desc) as 排名
from score;
-- 16. 查询学生的总成绩,并进行排名,总分重复时保留名次空缺
select sid,sum(cscore),
rank() over (order by sum(cscore) desc) as 排名
from score
group by sid;

 以上为第二次练习

-- 17. 统计各科成绩各分数段人数:课程编号,课程名称,[100-85],[85-70],[70-60],[60-0] 及所占百分比
select t.cid,c.cname,sum(case when t.cscore>85 and t.cscore<=100 then 1 else 0 end) as '[100-85]',
CONCAT(round(100*sum(case when t.cscore>85 and t.cscore<=100 then 1 else 0 end)/count(t.sid),2),'%') as '[100-85]百分比',
sum(case when t.cscore>70 and t.cscore<=85 then 1 else 0 end) as '[85-70]',
CONCAT(round(100*sum(case when t.cscore>70 and t.cscore<=85 then 1 else 0 end)/count(t.sid),2),'%') as '[85-70]百分比',
sum(case when t.cscore>60 and t.cscore<=70 then 1 else 0 end) as '[70-60]',
CONCAT(round(100*sum(case when t.cscore>60 and t.cscore<=70 then 1 else 0 end)/count(t.sid),2),'%') as '[70-60]百分比',
sum(case when t.cscore>0 and t.cscore<=60 then 1 else 0 end) as '[60-0]',
CONCAT(round(100*sum(case when t.cscore>0 and t.cscore<=60 then 1 else 0 end)/count(t.sid),2),'%') as '[60-0]百分比'
from score as t,course as c
where t.cid=c.cid
group by t.cid

思考:select里的‘逗号’千万不要忘记;case when里的end 不要忘记;concat里的%记得加引号;表名重新命名后要在子查询里加上。

-- 18. 查询各科成绩前三名的记录
#保留名词空缺

select * from (select cid,sid,cscore,
rank() over (PARTITION by cid order by cscore desc) as 排名
from score) as t
where 排名 <= 3;

#不保留名次空缺
select * from
	(select score.*, 
	dense_rank() over (partition by cid order by cscore desc) as 排名
	from score) as t1
where 排名 <= 3;

思考:不能用limit,因为要知道每个课程的前三名。窗口函数在select里面,所以还无法进行筛选,所以要另起表。而且在外面groupby 与partition by 不一样,前者只会出现每个课程一条,后者会出现每门课的所有成绩的排名

-- 19. 查询每门课程被选修的学生数
select cid,count(DISTINCT sid)
from score
group by cid;

较简单

-- 20. 查询出只选修两门课程的学生学号和姓名
方法一
select s.sid,stu.sname,count(DISTINCT s.cid) as 选修课程数
from score as s,student as stu
where s.sid=stu.sid
group by sid
having 选修课程数 = 2;

方法二 子查询
select sid, sname from student where sid in
	(select sid from score
	group by sid
	having count(cid) = 2);

思考:groupby与having的运用;子查询的话在实际select的内容可以在一个表里面查询到,那用where+另一查询结合,也是非常nice

-- 21. 查询男生、女生人数
select Ssex,count(distinct sid)
from student
group by student.Ssex

思考:COUNT(*)函数结果集每个分组内的所有行进行计数

-- 22. 查询名字中含有「风」字的学生信息
select sname 
from student
where sname like '%风%'

思考:where与like 进行结合

-- 23. 查询同名同性学生名单,并统计同名人数
*错误
select st1.sname,count(st1.sname)
from student as st1
join student as st2
on st1.sname=st2.sname;

--正确
select sname,count(*) as '人数'
from student
group by sname
having 人数 > 1;

思考:得弄清楚啊,只是需要group by

-- 24. 查询 1990 年年出生的学生名单
select *
from student
where Sage like '1990%'
select * from student
where YEAR(sage) = 1990;

可以先试试用year转换

-- 25. 查询每门课程的平均成绩,结果按平均成绩降序排列,平均成绩相同时,按课程编号升序排列
select cid,avg(cscore) as 平均成绩
from score
group by cid
order by 平均成绩,cid;

思考:orderby后面可以跟2个

-- 26. 查询平均成绩大于等于 85 的所有学生的学号、姓名和平均成绩
select s.sid,stu.sname,avg(cscore) as 平均成绩
from score as s
join student as stu
on s.sid=stu.sid
group by s.sid
having 平均成绩 >= 85;

思考:2表连接,groupby+having+聚合函数。用聚合函数时多想想要不要groupby

 

-- 27. 查询课程名称为「数学」,且分数低于 60 的学生姓名和分数
-- 方法一,多表内连接
select student.Sname,score.cscore
from student,score,course
where student.sid=score.sid
and score.cid=course.cid
and course.cname='数学'
and score.cscore<60;
-- 方法二,where做筛选条件
select student.sname, cscore from score 
join student 
on score.sid = student.sid
where cid = (select cid from course where cname = '数学') and cscore < 60;
-- 方法三,group by+having
select student.sname, cscore from score 
left join student 
on score.sid = student.sid
where cid = (select cid from course where cname = '数学') 
group by cscore
having cscore < 60;

1.表连接与2.表连接与子查询结合

-- 28. 查询所有学生的课程及分数情况(存在学生没成绩,没选课的情况)
select *
from student
left join score
on student.sid=score.sid;

简单

-- 29. 查询任何一门课程成绩在 70 分以上的姓名、课程名称和分数
-- 方法一Where直接筛选
select student.Sname,course.cname,score.cscore
from student,score,course
where student.sid=score.sid
and score.cid=course.cid
and score.cscore>70;
-- 方法二,三表连接
select sname, cname, cscore from student
inner join score on score.sid = student.sid
inner join course on course.cid = score.cid
where cscore >= 70;
-- 方法三,三表连接方法2
select sname, cname, cscore from student
inner join (select * from score where cscore >= 70) as t1 on student.sid = t1.sid
inner join course on course.cid = t1.cid;

多表连接的几种方法

-- 30. 查询不及格的课程及学生名,学号,按课程号从大到小排列
select score.cid,student.sname,student.sid
from student
left join score
on student.sid=score.sid
where score.cscore<60
order by score.cid;

简单表连接+where+order by

-- 31. 查询课程编号为 01 且课程成绩在 80 分及以上的学生的学号和姓名
select sid,Sname
from student
where sid in (select sid from score where cid=01 and score.cscore>=80);

与27题类似

-- 32. 求每门课程的学生人数
select cid,count(sid)
from score
group by cid;

思考:Group by用cid分组,会返回3行结果,分别是cid = 01/02/03

COUNT(sid)对Group by返回的每一组结果进行计数

-- 33. 成绩不重复,查询选修「张三」老师所授课程的学生中,成绩最高的学生信息及其成绩
-- 方法一,Limit
select student.*,score.cscore
from student,score,course,teacher
where student.sid=score.sid
and score.cid=course.cid
and course.tid=teacher.tid
and teacher.tname='张三'
order by score.cscore desc
limit 1;
-- 方法二,MAX
select cname, tname, sname, cscore, max(distinct cscore) as 最高分
from student, course, score, teacher
where score.sid = student.sid
and score.cid = course.cid
and teacher.tid = course.tid
and tname = '张三';

成绩不重复时,求最大值,max或者limit

-- 34. 成绩有重复的情况下,查询选修「张三」老师所授课程的学生中,成绩最高的学生信息及其成绩
-- 方法一,窗口函数
select * from (select student.*,score.cscore,
rank() over (order by score.cscore desc) as 排名
from student,score,course,teacher
where student.sid=score.sid
and score.cid=course.cid
and course.tid=teacher.tid
and teacher.tname='张三') t1
where t1.排名<=1;
-- 方法二,子查询
select cname, tname, sname, cscore
from student, course, score, teacher
where score.sid = student.sid
and score.cid = course.cid
and teacher.tid = course.tid
and tname = '张三'
and cscore in
    (select max(cscore) as 最高分
    from student, course, score, teacher
    where score.sid = student.sid
    and score.cid = course.cid
    and teacher.tid = course.tid
    and tname = '张三');

成绩重复时,上面不再适用,子查询出最高成绩分数作为条件进行筛选

-- 35. 查询不同课程成绩相同的学生的学生编号、课程编号、学生成绩
-- 错误
select s1.sid,s1.cid,s1.cscore
from score as s1
inner join score as s2
on s1.cscore=s2.cscore
-- 题意:一个学生至少选了2门课,每门课的成绩一样
-- 方法一,子查询+自交
select * from score where sid in (select t1.sid from score t1
join score t2
on t1.sid=t2.sid
where t1.cid<>t2.cid
and t1.cscore=t2.cscore)
-- 方法二,子查询+聚合函数:查出选课数大于1的学生,并且不同课程分数的最大值=最小值(即分数相等)
select * from score where sid in 
(select sid
from score
GROUP BY sid
having min(cscore)=max(cscore) and count(*)>1);

思考:

-- 36. 查询所有课程成绩第二名到第三名的学生信息及课程成绩
-- 自己写
select student.*,t1.cscore
from student
left join
(select sid,cid,cscore,
rank() over (partition by cid order by cscore desc) as 排名
from score) as t1
on student.sid=t1.sid
where t1.排名>=2
and t1.排名<=3;
-- 参考答案
select sname, t1.* from student
inner join
    (select score.*, 
    dense_rank() over (partition by cid order by cscore desc) as 排名 from score) as t1
on student.sid = t1.sid
where 排名 in (2,3)
order by cid, 排名;

思考:既大于又小于,那么用in,rank窗口函数

-- 37. 统计每门课程的学生选修人数(超过 5 人的课程才统计)
select cid,count(sid) as '选修人数'
from score
group by cid
having 选修人数 >= 5;

简单

-- 38. 检索至少选修两门课程的学生学号
select sid ,count(cid) as '选修课程数'
from score
group by sid
having 选修课程数 >= 2;

简单

-- 39. 查询选修了全部课程的学生信息
SELECT *
from student
where sid in (
select sid from score
group by sid
having count(cid)=(select count(cid) from course))

参考第7题

-- 40. 查询各学生的年龄,只按年份来算
select sid,sname,(year(curdate())-year(sage)) as 年龄
from student;

 

-- 41. 按照出生日期来算,如果当前月日 < 出生年月的月日,年龄减一
select sid,sname,timestampdiff(year,sage,curdate()) as 年龄
from student;

 

-- 42. 查询本周过生日的学生
select *, MONTH(Sage) as 月, DAY(Sage) as 日,
CONCAT_WS('-',Year(now()),MONTH(Sage),DAY(Sage)) as 拼接日期 
from student 
having WEEK('2020-05-20') = WEEK(拼接日期);
-- 错误
select * from student 
where week(student.sage) = mweek('2021-05-21');

 

-- 43. 查询下周过生日的学生
select *,month(Sage) as 月,day(sage) as 日,
CONCAT_WS('-',year(now()),month(Sage),day(sage)) as 拼接日期
from student
having week('2021-05-20')+1=week(拼接日期);

 

-- 44. 查询本月过生日的学生
select *,month(sage) as 月, day(sage) as 日,
CONCAT_WS('-',year(now()),month(sage),day(sage)) as 拼接日期
from student
having month('2021-05-20')=month(拼接日期);

select * from student 
where month(student.sage) = month('2021-05-21');

 

-- 45. 查询下月过生日的学生
select *,month(sage) as 月, day(sage) as 日,
CONCAT_WS('-',year(now()),month(sage),day(sage)) as 拼接日期
from student
having month('2021-05-20')+1=month(拼接日期);

select * from student 
where month(student.sage) = month('2021-05-21') + 1;