(补充至第15题)
有疑问:第9题
注:一个SQL语句写完后,用“;”结尾
练习数据
--1.学生表 Student(SId,Sname,Sage,Ssex) --SId 学生编号,Sname 学生姓名,Sage 出生年月,Ssex 学生性别
--2.课程表 Course(CId,Cname,TId) --CId --课程编号,Cname 课程名称,TId 教师编号
--3.教师表 Teacher(TId,Tname) --TId 教师编号,Tname 教师姓名
--4.成绩表 SC(SId,CId,score) --SId 学生编号,CId 课程编号,score 分数
练习题目
1. ①查询" 01 "课程比" 02 "课程成绩高的学生的学生编号及课程分数
②查询同时存在" 01 "课程和" 02 "课程的情况
③查询存在" 01 "课程但可能不存在" 02 "课程的情况(不存在时显示为 null )
④查询不存在" 01 "课程但存在" 02 "课程的情况
解:
①思路:先分别把有01课程和02课程的分别查询出来,查询时使用别名AS,再把相同的学生编号的课程01和课程02成绩进行比较,即有两个并列条件——01成绩>02成绩,两门成绩对应的学生编号相等
①答案:
SELECT t1.SId, t1.score AS '01',t2.score AS '02'
FROM
(SELECT SId, scoreFROM SCWHERE CId = '01' ) AS t1,
(SELECT SId, scoreFROM SCWHERE CId = '02' ) AS t2
WHERE t1.SId = t2.SId AND t1.score > t2.score;
②思路:先把01课程存在和02课程存在分别查询出来,再取两门课同时存在,即两门课都有相同的SId
②答案:
SELECT *
FROM
(SELECT SId,score FROM SC WHERE SC.CId=‘01’) AS t1,
(SELECT SId,score FROM SC WHERE SC.CId=‘02’) AS t2
WHERE t1.SId=t2.SId;
③思路:SId作为主键是一定有的,课程编号和成绩不一定有,01存在02不存在,采取左联结取交集或只有01的部分,只用保证SId一致即可
③答案:
SELECT *
FROM
(SELECT *FROM SCWHERE CId = '01' ) AS t1
LEFT JOIN
(SELECT *FROM SCWHERE CId = '02' ) AS t2
ON t1.SId = t2.SId;
④思路:课程01不存在,可以先把存在的查询出来,再用NOT IN反选出不存在的,同时并列条件课程02存在
④答案:
SELECT *
FROM SC
WHERE SC.SId NOT IN (SELECT SId FROM SC WHERE SC.CId=‘01’)
AND SC.CId=‘02’;
2. 查询平均成绩大于等于 60 分的同学的学生编号和学生姓名和平均成绩
解:
思路:在SC表格中以平均成绩大于60分为条件,检索出学生编号SId,再以此编号为条件,在Student表格出查询出学生姓名,同时在前一个表格中查询出平均成绩,需要用INNER JOIN将两个表连接起来
注意:INNER JOIN接ON,GROUP BY接HAVING
答案:
SELECT Student.SId,Student.Sname,score_avg
FROM Student INNER JOIN (
SELECT SC.SId,AVG(SC.score) AS score_avg
FROM SC
GROUP BY SC.SId
HAVING AVG(SC.score)>=60) AS t1
ON Student.SId=t1.SId;
也可直接用“逗号”连接,不用INNER JOIN。选出SC表格中平均分数高于60分的SId和平均分→连接Student表→选出题目要的三个参数
SELECT t.SId, Student.Sname, t.score_avg
FROM
(SELECT SId, AVG(score) AS score_avg FROM SCGROUP BY SIdHAVING score_avg >= 60) AS t,
Student
WHERE t.SId = Student.SId;
3. 查询在 SC 表存在成绩的学生信息
解:
思路1:在SC里查询出存在成绩(IS NOT NULL)的学生id→从Student表里查询出对应的学生信息。注意每行为一门课程的成绩,因此SID会存在重复的情况,查询时注意查询唯一值(DISTINCT)。
答案1:
SELECT Student.*
FROM Student,(SELECT Distinct SId FROM SC WHERE score IS NOT NULL) AS t
WHERE Student.SId = t.SId;
思路2:SC表中筛选出唯一值的SId→再在Student表中筛选出对应信息
答案2:
SELECT *
FROM Student
WHERE Student.SId IN (SELECT DISTINCT SId FROM SC);
4. ①查询所有同学的学生编号、学生姓名、选课总数、所有课程的总成绩(没成绩的显示为 null )
②查有成绩的学生信息
解:
①思路:按学生编号分组,查询出编号、选课总数、总成绩(不用加限定的表名)→两个表合并,查询出对应的参数
①答案:
SELECT Student.SId, Student.Sname, t.CId_count, t.score_sum
FROM Student,
(SELECT SId, SUM(score) AS score_sum, COUNT(CId) AS CId_countFROM SCGROUP BY SId) AS t
WHERE Student.SId = t.SId;
②思路:SC.score非空(IS NOT NULL),选出对应的SId——Student的SId在其中,IN
②答案:
SELECT Student.*
FROM Student
WHERE Student.SId IN (SELECT DISTINCT SId FROM SC WHERE score IS NOT NULL);
B表比A表大时(B表为后面的子查询),适合用EXISTS的写法:
SELECT Student.*
FROM Student
WHERE EXISTS(SELECT * FROM SC WHERE Student.SId=SC.SId);
注:EXISTS的用法参见《SQL里的IN和EXISTS》
5. 查询「李」姓老师的数量
解:
思路:用%通配符找到李X,再用count函数计算数量,注意通配符要和LIKE连用
答案:
SELECT COUNT(Tname)
FROM Teacher
WHERE Tname LIKE '李%';
6. 查询学过「张三」老师授课的同学的信息
解:
思路:Teacher.Tname——Teacher.TId=Course.TId——Course.CId=SC.CId——SC.SId=Student.SId——Student.*
答案:
SELECT *
FROM Student
WHERE SId IN
(SELECT SId FROM SC WHERE CId IN
(SELECT CId FROM Course WHERE TId IN
(SELECT TId FROM Teacher WHERE Tname = '张三')));
可更简化一点,直接使用AND:
SELECT DISTINCT Student.*
FROM Student, Course, Teacher, SC
WHERE Teacher.Tname = '张三'
AND Teacher.TId = Course.TId
AND Course.CId = SC.CId
AND SC.SId = Student.SId;
7. 查询没有学全所有课程的同学的信息
解:
思路:①在Course表中利用count函数计算出课程数量 ②在SC表中,以SId分组,用count函数计算出每个学生学的课程数量 ③利用having关键字选出②<①的SId ④到Student表中获取对应信息,注意用IN
答案:
SELECT Student.*
FROM Student
WHERE Student.SId IN (
SELECT SId
FROM SC
GROUP BY SId
HAVING COUNT(CId) < (SELECT COUNT(DISTINCT CId)FROM Course));
使用内部联结:
SELECT Student.*
FROM Student, SC
WHERE Student.SId = SC.SId
GROUP BY SC.SId
HAVING COUNT(CId) < (SELECT COUNT(DISTINCT CId)FROM Course);
以上两种方法得出来的结果不包括什么课都没选的同学,下面利用笛卡尔积可以把什么课都没选的同学查询出来:
SELECT DISTINCT Student.*
FROM (
(SELECT Student.SId, Course.CIdFROM Student, Course) AS t1
LEFT JOIN
(SELECT SC.SId, SC.CIdFROM SC) AS t2
ON t1.SId = t2.SId AND t1.CId = t2.CId),
Student
WHERE t1.SId = Student.SId AND t2.SId IS NULL;
8. 查询至少有一门课与学号为" 01 "的同学所学相同的同学的信息
解:
思路:内部联结Student和SC两个表(一定要用WHERE Student.SId=SC.SId),找出学号01所学的课程编号,之后用in关键字,取唯一值。
答案:
SELECT DISTINCT Student.*
FROM Student,SC
WHERE Student.SId=SC.SId
AND SC.CId IN (SELECT SC.CId FROM SC WHERE SC.SId=01)
使用自联结:
SELECT DISTINCT Student.*
FROM Student, SC AS s1, SC AS s2
WHERE Student.SId = s2.SId
AND s1.SId = '01'
AND s1.CId = s2.CId
9. 查询和" 01 "号的同学学习的课程 完全相同的其他同学的信息
注:关于此题更多思路,参见https://blog.csdn.net/qq_34303423/article/details/107237657
解:
思路:使用GROUP_CONCAT()函数(分组中的值拼接),把每个学生的CId按升序拼接起来进行对比,找出拼接后CId相等的SId
答案:
SELECT Student.*
FROM Student,
(SELECT SId FROM SC GROUP BY SId
HAVING GROUP_CONCAT(CId ORDER BY CId) =
(SELECT GROUP_CONCAT(CId ORDER BY CId) FROM SC GROUP BY SId HAVING SId = '01')
AND SId != '01') AS t
WHERE Student.SId = t.SId
思路2:所学课程数量相等,且课程id属于01所学的课程
【但此方法只能查找该学生学习课程数>=其他学生的情况,其他情况会出错,比如把01改成05后出错】
【子集从属关系不好操作】
答案:
SELECT DISTINCT Student.*
FROM
(SELECT DISTINCT t1.SId
FROM
(SELECT SId, COUNT(SC.CId) AS CId_count
FROM SC
GROUP BY SC.SId) AS t1, #每个学生所学课程数量
(SELECT COUNT(SC.CId) AS CId_01 FROM SC WHERE SId = '01') AS t2 #01学生所学课程数量
WHERE t1.CId_count = CId_01) AS t4, #和01所学课程数量相等的学生
SC, Student
WHERE t4.SId = Student.SId
AND Student.SId != '01'
AND SC.CId IN (SELECT CId FROM SC WHERE SId = '01') #01学生所学课程
10. 查询没学过"张三"老师讲授的任一门课程的学生姓名
解:
思路:张三老师所授课程的CId——上过对应CId课程的学生编号SId——查询不在上一步的SId里学生信息(NOT IN)
答案:
SELECT DISTINCT Student.SId, Student.Sname
FROM Student
WHERE SId NOT IN
(SELECT Student.SId
FROM Student, Course, Teacher, SC
WHERE Teacher.Tname = '张三'
AND Teacher.TId = Course.TId
AND SC.CId = Course.CId
AND Student.SId = SC.SId)
另一种用联结和EXISTS 的方法:
SELECT *
FROM Student
WHERE Student.SId NOT IN (
SELECT Student.SId FROM Student LEFT JOIN SC ON Student.SId=SC.SId
WHERE EXISTS
(SELECT * FROM Teacher,Course WHERE Teacher.Tname='张三'
AND Teacher.TId=Course.TId
AND Course.CId=SC.CId))
11. 查询两门及其以上不及格课程的同学的学号,姓名及其平均成绩
解:
思路:找出小于60分的学生ID及成绩——按学生ID分组,过滤出数量大于等于2的学生ID——再从SC表GROUP BY计算平均分数【确保该学生所有分数都被纳入计算】
答案:注意不能把条件“score<60”放在开始的SELECT语句里,应该在后面用WHERE表示。
SELECT Student.SId, Student.Sname, AVG(SC.score)
FROM Student, SC,
(SELECT SId,COUNT(score) AS score_c
FROM
(SELECT SId, score FROM SC WHERE score < 60) AS t1
GROUP BY SId
HAVING score_c >= 2) AS t2 #两门以上不及格的学生ID
WHERE Student.SId = t2.SId
AND Student.SId = SC.SId
GROUP BY SC.SId
12. 检索" 01 "课程分数小于 60,按分数降序排列的学生信息
解:
思路:最基础的语句,把几个条件用AND连接好即可
答案:
SELECT Student.*
FROM Student,SC
WHERE Student.SId=SC.SId
AND SC.CId='01'
AND SC.score<60
ORDER BY score DESC
13. 按平均成绩从高到低显示所有学生的所有课程的成绩以及平均成绩
解:
思路:计算每位学生的平均成绩——以SC为基准联结——排序
答案:
SELECT t.SId, SC.CId, SC.score, t.score_avg
FROM SC
LEFT JOIN
(SELECT SId,AVG(score) AS score_avg
FROM SC
GROUP BY SId) AS t
ON t.SId = SC.SId
ORDER BY score_avg DESC
但上述方式最后生成的表格不太科学,因为原数据是将成绩一条条导入的,并不是按照学生编号把成绩统一到一条数据里,因而需要把竖向的数据横向合并显示。主要用到了LEFTJOIN来连接各个数据,平均成绩也用此方法连接。
【虽然此操作最后显示结果是满意的,但不好的地方在于语句中直接使用了CId的编号(在此操作前查看了CId的的数值),不知道是否可以不进行此操作,即不进行CId指定直接把竖向数据横向合并显示。】
答案2:
SELECT DISTINCT SC.SId,d.p AS AVG,a.score AS score_01 ,b.score AS score_02 ,c.score AS score_03
FROM SC
LEFT JOIN (SELECT SId,CId,score FROM SC WHERE CId='01') AS a
ON SC.SId=a.SId
LEFT JOIN (SELECT SId,CId,score FROM SC WHERE CId='02') AS b
ON SC.SId=b.SId
LEFT JOIN (SELECT SId,CId,score FROM SC WHERE CId='03') AS c
ON SC.SId=c.SId
LEFT JOIN (SELECT SId,AVG(score) AS p FROM SC GROUP BY SC.SId) AS d
ON SC.SId=d.SId
ORDER BY d.p
最后的查询结果如图所示:
答案3:和方法2输出结果一致,使用CASE WHEN THEN END更简洁
SELECT SId,
SUM(CASE WHEN CId = '01' THEN score ELSE NULL END) AS score_01,
SUM(CASE WHEN CId = '02' THEN score ELSE NULL END) AS score_02,
SUM(CASE WHEN CId = '03' THEN score ELSE NULL END) AS score_03,
AVG(score)
FROM SC
GROUP BY SId
ORDER BY AVG(score) DESC
14. 查询各科成绩最高分、最低分和平均分:以如下形式显示:课程 ID,课程 name,最高分,最低分,平均分,及格率,中等率,优良率,优秀率及格为>=60,中等为:70-80,优良为:80-90,优秀为:>=90 要求输出课程号和选修人数,查询结果按人数降序排列,若人数相同,按课程号升序排列
解:
思路:输出项——多条件判断CASE WHEN THEN END——FROM……INNER JOIN (ON)——分组GROUP BY——排序
答案:
SELECT SC.CId AS 课程ID,Course.Cname AS 课程名,MAX(score)AS 最高分,MIN(score) AS 最低分,AVG(score) AS 平均分,count(SC.CId) AS 选修人数,
SUM(CASE WHEN SC.score>=60 THEN 1 ELSE 0 END)/COUNT(*)AS 及格率,
SUM(CASE WHEN SC.score>=70 AND SC.score<80 THEN 1 ELSE 0 END)/COUNT(*)AS 中等率,
SUM(CASE WHEN SC.score>=80 AND SC.score<90 THEN 1 ELSE 0 END)/COUNT(*)AS 优良率,
SUM(CASE WHEN SC.score>=90 THEN 1 ELSE 0 END)/COUNT(*)AS 优秀率
FROM SC INNER JOIN Course
ON SC.CId=Course.CId
GROUP BY SC.CId
ORDER BY count(score),SC.CId ASC
15. ①按各科成绩进行排序,并显示排名, Score 重复时保留名次空缺
②按各科成绩进行排序,并显示排名, Score 重复时合并名次
【没太看懂这个题,暂时按①1233367 ②1233345这种理解来解答】
解:
①思路:@ 定义变量,:= 赋值,= 判定相等。IF(条件,A,B),满足条件,返回A,不满足返回B。
事先赋值:(SELECT @curRank := 0,@precRank := NULL, @incRank := 1) AS r
从上往下执行:
@curRank := IF(@prevRank =score,@curRank,@incRank) AS rank,
@incRank := @incRank + 1,
@prevRank := score
第一次执行@curRank时@prevRank为NULL,不等于score,因此返回@incRank(此时为初始值1),随之@incRank+1,@prevRank赋值为第一个score;第二次执行,如果第二个score和第一个score不相等,则返回上次计算后的@incRank(即@incRank+1 = 2),如果相等,则返回上一次的@curRank(即1);……即以@incRank为循环
①答案:
SELECT SId, CId, score, rank
FROM
(SELECT *,
@curRank := IF(@prevRank =score,@curRank,@incRank) AS rank,
@incRank := @incRank + 1,
@prevRank := score
FROM SC, (SELECT @curRank := 0,@precRank := NULL, @incRank := 1) AS r
ORDER BY score DESC) AS s
②思路:使用CASE WHEN TEHN,当@prevRank = score THEN @curRank不满足时执行之后的WHEN @prevRank := score THEN @curRank := @curRank + 1,依次循环
②答案:
SELECT SId, CId, score,
CASE
WHEN @prevRank = score THEN @curRank
WHEN @prevRank := score THEN @curRank := @curRank + 1
END AS rank
FROM SC, (SELECT @curRank := 0, @prevRank := NULL) AS r
ORDER BY score DESC
③附最简单的排序,直接按123456789来排序的方法:
SELECT SId, CId, score,@curRank := @curRank + 1 AS rank
FROM SC, (SELECT @curRank := 0) AS q
ORDER BY score desc
或事先用set定义好,但此方法会执行两次查询,建议还是用上面的
SET @curRank := 0;
SELECT *,@curRank := @curRank + 1 AS rank
FROM SC
ORDER BY score desc
16. ① 查询学生的总成绩,并进行排名,总分重复时保留名次空缺
②查询学生的总成绩,并进行排名,总分重复时不保留名次空缺
解:
--1.学生表 Student(SId,Sname,Sage,Ssex) --SId 学生编号,Sname 学生姓名,Sage 出生年月,Ssex 学生性别
--2.课程表 Course(CId,Cname,TId) --CId --课程编号,Cname 课程名称,TId 教师编号
--3.教师表 Teacher(TId,Tname) --TId 教师编号,Tname 教师姓名
--4.成绩表 SC(SId,CId,score) --SId 学生编号,CId 课程编号,score 分数
①思路:
①答案:
②思路:
②答案:
17. 统计各科成绩各分数段人数:课程编号,课程名称,[100-85],[85-70],[70-60],[60-0] 及所占百分比
解:
思路:
答案:
18. 查询各科成绩前三名的记录
解:
思路:
答案:
19. 查询每门课程被选修的学生数
解:
思路:
答案:
20. 查询出只选修两门课程的学生学号和姓名
解:
思路:
答案:
21. 查询男生、女生人数
解:
思路:
答案:
22. 查询名字中含有「风」字的学生信息
解:
思路:
答案:
23. 查询同名同性学生名单,并统计同名人数
解:
思路:
答案:
24. 查询 1990 年出生的学生名单
解:
思路:
答案:
25. 查询每门课程的平均成绩,结果按平均成绩降序排列,平均成绩相同时,按课程编号升序排列
解:
思路:
答案:
26. 查询平均成绩大于等于 85 的所有学生的学号、姓名和平均成绩
解:
思路:
答案:
27. 查询课程名称为「数学」,且分数低于 60 的学生姓名和分数
解:
思路:
答案:
28. 查询所有学生的课程及分数情况(存在学生没成绩,没选课的情况)
解:
思路:
答案:
29. 查询任何一门课程成绩在 70 分以上的姓名、课程名称和分数
解:
思路:
答案:
30. 查询不及格的课程
解:
思路:
答案:
31. 查询课程编号为 01 且课程成绩在 80 分以上的学生的学号和姓名
解:
思路:
答案:
32. 求每门课程的学生人数
解:
思路:
答案:
33. 成绩不重复,查询选修「张三」老师所授课程的学生中,成绩最高的学生信息及其成绩
解:
思路:
答案:
34. 成绩有重复的情况下,查询选修「张三」老师所授课程的学生中,成绩最高的学生信息及其成绩
解:
思路:
答案:
35. 查询不同课程成绩相同的学生的学生编号、课程编号、学生成绩
解:
思路:
答案:
36. 查询每门功成绩最好的前两名
解:
思路:
答案:
37. 统计每门课程的学生选修人数(超过 5 人的课程才统计)
解:
思路:
答案:
38. 检索至少选修两门课程的学生学号
解:
思路:
答案:
39. 查询选修了全部课程的学生信息
解:
思路:
答案:
40. 查询各学生的年龄,只按年份来算
解:
思路:
答案:
41. 按照出生日期来算,当前月日 < 出生年月的月日则,年龄减一
解:
思路:
答案:
42. 查询本周过生日的学生
解:
思路:
答案:
43. 查询下周过生日的学生
解:
思路:
答案:
44. 查询本月过生日的学生
解:
思路:
答案:
45. 查询下月过生日的学生
解:
思路:
答案: