SQL经典50题
selfLabel:SQL初学/兴趣自学/非相关专业在职
学习不足半月,纯记录自学过程。
所用软件:MySQL
经典50题数据库模型图:
个人习惯先建立模型,再将实体模型转化为代码。
插入数据:
注意在模型基础上插入数据的先后顺序,因为模型中已经定义了父子表,如果首先插入子表数据会出现错误提示,错误代码1452。
--插入学生表测试数据
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' , '女');
--课程表测试数据
insert into Course values('01' , '语文' , '02');
insert into Course values('02' , '数学' , '01');
insert into Course values('03' , '英语' , '03');
--教师表测试数据
insert into Teacher values('01' , '张三');
insert into Teacher values('02' , '李四');
insert into Teacher values('03' , '王五');
--成绩表测试数据
insert into Score values('01' , '01' , 80);
insert into Score values('01' , '02' , 90);
insert into Score values('01' , '03' , 99);
insert into Score values('02' , '01' , 70);
insert into Score values('02' , '02' , 60);
insert into Score values('02' , '03' , 80);
insert into Score values('03' , '01' , 80);
insert into Score values('03' , '02' , 80);
insert into Score values('03' , '03' , 80);
insert into Score values('04' , '01' , 50);
insert into Score values('04' , '02' , 30);
insert into Score values('04' , '03' , 20);
insert into Score values('05' , '01' , 76);
insert into Score values('05' , '02' , 87);
insert into Score values('06' , '01' , 31);
insert into Score values('06' , '03' , 34);
insert into Score values('07' , '02' , 89);
insert into Score values('07' , '03' , 98);
题目:
1、查询课程编号为“01”的课程比“02”的课程成绩高的所有学生的学号(重点)
2、查询平均成绩大于60分的学生的学号和平均成绩(简单,第二道重点)
3、查询所有学生的学号、姓名、选课数、总成绩(不重要)
4、查询姓“猴”的老师的个数(不重要)
5、查询没学过“张三”老师课的学生的学号、姓名(重点)
6、查询学过“张三”老师所教的所有课的同学的学号、姓名(重点)
7、查询学过编号为“01”的课程并且也学过编号为“02”的课程的学生的学号、姓名(重点)
8、查询课程编号为“02”的总成绩(不重点)
9、查询所有课程成绩小于60分的学生的学号、姓名
10、查询没有学全所有课的学生的学号、姓名(重点)
11、查询至少有一门课与学号为“01”的学生所学课程相同的学生的学号和姓名(重点)
12、查询和“01”号同学所学课程完全相同的其他同学的学号(重点)
13、查询没学过"张三"老师讲授的任一门课程的学生姓名 和47题一样(重点,能做出来)
14题无
15、查询两门及其以上不及格课程的同学的学号,姓名及其平均成绩(重点)
16、检索"01"课程分数小于60,按分数降序排列的学生信息(和34题重复,不重点)
17、按平均成绩从高到低显示所有学生的所有课程的成绩以及平均成绩(重重点与35一样)
18.查询各科成绩最高分、最低分和平均分:以如下形式显示:课程ID,课程name,最高分,最低分,平均分,及格率,中等率,优良率,优秀率
--及格为>=60,中等为:70-80,优良为:80-90,优秀为:>=90 (超级重点)
19、按各科成绩进行排序,并显示排名(重点row_number)
20、查询学生的总成绩并进行排名(不重点)
21 、查询不同老师所教不同课程平均分从高到低显示(不重点)
22、查询所有课程的成绩第2名到第3名的学生信息及该课程成绩(重要 25类似)
23、使用分段[100-85],[85-70],[70-60],[<60]来统计各科成绩,分别统计各分数段人数、课程ID和课程名称(重点和18题类似)
24、查询学生平均成绩及其名次(同19题,重点)
25、查询各科成绩前三名的记录(不考虑成绩并列情况)(重点 与22题类似)
26、查询每门课程被选修的学生数(不重点)
27、查询出只有两门课程的全部学生的学号和姓名(不重点)
28、查询男生、女生人数(不重点)
29、 查询名字中含有"风"字的学生信息(不重点)
30题忽略掉
31、查询1990年出生的学生名单(重点year)
32、查询平均成绩大于等于85的所有学生的学号、姓名和平均成绩(不重要)
33、查询每门课程的平均成绩,结果按平均成绩升序排序,平均成绩相同时,按课程号降序排列(不重要)
34、查询课程名称为"数学",且分数低于60的学生姓名和分数(不重点)
35、查询所有学生的课程及分数情况(重点)
36、查询任何一门课程成绩在70分以上的姓名、课程名称和分数(重点)
37、查询不及格的课程并按课程号从大到小排列(不重点)
38、查询课程编号为03且课程成绩在80分以上的学生的学号和姓名(不重要)
39、求每门课程的学生人数(不重要,与26题重复)
40、查询选修“张三”老师所授课程的学生中成绩最高的学生姓名及其成绩(重要top/limit)
41、查询不同课程成绩相同的学生的学生编号、课程编号、学生成绩 (重点)
42、查询每门功成绩最好的前两名(同22和25题)
43、统计每门课程的学生选修人数(超过5人的课程才统计)。要求输出课程号和选修人数,查询结果按人数降序排列,若人数相同,按课程号升序排列(不重要)
44、检索至少选修两门课程的学生学号(不重要)
45、 查询选修了全部课程的学生信息(重点)
46、查询各学生的年龄(精确到月份)
47、查询没学过“张三”老师讲授的任一门课程的学生姓名
48、查询两门以上不及格课程的同学的学号及其平均成绩
49、查询本月过生日的学生
个人答案:
1、查询课程编号为“01”的课程比“02”的课程成绩高的所有学生的学号(重点)
SELECT s_id FROM score ss WHERE (SELECT score FROM score WHERE s_id = ss.s_id AND c_id = 1) > (SELECT score FROM score WHERE s_id = ss.s_id AND c_id = 2) GROUP BY s_id①用了两次自相关查询,同理,也可以进行两次内连接;
②select之后的数据可以直接进行判断比较;
③group by 也可以换成 select distinct 放前面去重。
SELECT DISTINCT s_id FROM score ss JOIN (SELECT * FROM score WHERE c_id = 1) s1 USING(s_id) JOIN (SELECT * FROM score WHERE c_id = 2) s2 USING(s_id) WHERE s1.score > s2.score2、查询平均成绩大于60分的学生的学号和平均成绩(简单,第二道重点)
SELECT s_id, AVG(score) FROM score GROUP BY s_id HAVING AVG(score) > 60
注意是先按学生分完组再进行平均值判断,所以是先group by后having,而不能先where再group by。
***扩展:如果查询平均成绩小于60分的学生,包括有成绩和没成绩的,那么
SELECT s_id, IFNULL(AVG(score),0) FROM student st LEFT JOIN score sc USING(s_id) GROUP BY st.s_id HAVING IFNULL(AVG(score),0) < 60这里比较特殊的点就是没成绩的学生学号也要输出,因此比上一种情况多了两个步骤:①与student表外连接;②用ifnull对avg进行判断
3、查询所有学生的学号、姓名、选课数、总成绩(不重要)
SELECT st.s_id, name, COUNT(DISTINCT c_id), SUM(score) FROM student st JOIN score sc USING(s_id) GROUP BY s_iddistinct可要可不要
4、查询姓“猴”的老师的个数(不重要)
SELECT COUNT(*) FROM teacher WHERE name LIKE '猴%'姓猴 所以%放猴之后
5、查询没学过“张三”老师课的学生的学号、姓名(重点)
SELECT student.s_id, student.name FROM student WHERE student.s_id NOT IN (SELECT st.s_id FROM student st LEFT JOIN score sc USING(s_id) LEFT JOIN course c USING(c_id) LEFT JOIN teacher t USING(t_id) WHERE t.name IN ('张三'))应用子查询,首先查出来学习张三老师课的所有s_id(子查询就是完整的查询学过“张三”老师课的学生学号),然后排除掉这些id
6、查询学过“张三”老师所教的所有课的同学的学号、姓名(重点)
SELECT st.s_id, st.name FROM student st JOIN score sc USING(s_id) JOIN course c USING(c_id) JOIN teacher t USING(t_id) WHERE t.name = '张三'就是上道题子查询部分
7、查询学过编号为“01”的课程并且也学过编号为“02”的课程的学生的学号、姓名(重点)
SELECT st.s_id, st.name FROM student st WHERE ( (SELECT s_id FROM score s1 WHERE c_id = 1 AND s_id = st.s_id) = (SELECT s_id FROM score s2 WHERE c_id = 2 AND s_id = st.s_id) )这种解法跟第1题的第二种解法类似,建立相关子查询进行直接判断
SELECT s_id, name FROM student WHERE s_id IN ( SELECT a.s_id FROM (SELECT s_id FROM score WHERE c_id = 1) a JOIN (SELECT s_id FROM score WHERE c_id = 2) b ON a.s_id = b.s_id )不太习惯这种解法,子查询次数太多,但也是一种思路
***扩展:如果要查询学过编号01的课程但是没有学过编号为02的课程的学生信息,那么用第一种思路
SELECT st.s_id, st.name FROM student st WHERE s_id IN (SELECT s_id FROM score WHERE c_id = 1) AND s_id NOT IN (SELECT s_id FROM score WHERE c_id = 2)
8、查询课程编号为“02”的总成绩(不重点)
SELECT SUM(score) FROM score WHERE c_id = 2
9、查询所有课程成绩小于60分的学生的学号、姓名
SELECT * FROM student JOIN score USING(s_id) WHERE score < 60
10、查询没有学全所有课的学生的学号、姓名(重点)
SELECT st.s_id, st.name FROM student st JOIN score USING(s_id) GROUP BY st.s_id HAVING COUNT(DISTINCT c_id) < (SELECT COUNT(DISTINCT c_id) FROM course)一般来说,having中所使用的判断参数必须是select里提到的,但是有一个例外,就是having可以有select没定义的聚合函数。这道题还挺好的
11、查询至少有一门课与学号为“01”的学生所学课程相同的学生的学号和姓名(重点)
SELECT DISTINCT st.s_id, st.name FROM student st JOIN score sc USING(s_id) WHERE c_id IN ( SELECT c_id FROM score WHERE s_id = 1 ) AND st.s_id <> 1注意最后的s_id <> 1,其他都比较简单
12、查询和“01”号同学所学课程完全相同的其他同学的学号(重点)
SELECT DISTINCT st.s_id, st.name FROM student st JOIN score sc USING(s_id) -- WHERE s_id NOT IN -- (SELECT DISTINCT s_id FROM score -- WHERE c_id NOT IN (SELECT c_id FROM score WHERE s_id = 1)) GROUP BY st.s_id HAVING COUNT(DISTINCT c_id) = (SELECT COUNT(DISTINCT c_id) FROM score WHERE s_id = 1) AND st.s_id <> 1 AND s_id NOT IN (SELECT DISTINCT s_id FROM score WHERE c_id NOT IN (SELECT c_id FROM score WHERE s_id = 1))这道题逻辑比较巧妙,一共有两个判断,判断1:课程总数量一致,判断2:查找的同学没上过01号同学没学过的课程,having的第三个条件和注释掉的where语句都是判断2的代码,两种方式都可以实现
13、查询没学过"张三"老师讲授的任一门课程的学生姓名 和47题一样(重点,能做出来)
SELECT DISTINCT st.s_id, name FROM student st LEFT JOIN score USING(s_id) WHERE s_id NOT IN( SELECT s_id FROM score WHERE c_id IN( SELECT c.c_id FROM course c JOIN teacher t USING(t_id) WHERE t.name = '张三'))先查找张三老师讲授的所有课程id,然后在score表中找到所有学习这些课程的学生学号,然后通过一次外连接排除掉这些学过的学生
15、查询两门及其以上不及格课程的同学的学号,姓名及其平均成绩(重点)
SELECT DISTINCT s_id, st.name, AVG(score) FROM score JOIN student st USING(s_id) WHERE score < 60 GROUP BY s_id HAVING COUNT(*) >= 2一步步做比较简单
16、检索"01"课程分数小于60,按分数降序排列的学生信息(和34题重复,不重点)
SELECT st.s_id, st.name FROM student st JOIN score sc USING (s_id) WHERE c_id = 1 AND score < 60 ORDER BY score DESC
17、按平均成绩从高到低显示所有学生的所有课程的成绩以及平均成绩(重重点与35一样)
SELECT s_id, c_id, score, (SELECT AVG(score) FROM score GROUP BY s_id HAVING s_id = s.s_id) avg FROM score s ORDER BY avg DESC这种解法满足题目要求,但是显示方式不够清晰,也跟实际情况不符,按照真实的显示情况,应为这样:
SELECT DISTINCT s_id '学号', (SELECT name FROM student WHERE s_id = s.s_id) '姓名', (SELECT score FROM score WHERE c_id = 1 AND s_id =s.s_id) '语文', (SELECT score FROM score WHERE c_id = 2 AND s_id =s.s_id) '数学', (SELECT score FROM score WHERE c_id = 3 AND s_id =s.s_id) '英语', (SELECT AVG(score) FROM score GROUP BY s_id HAVING s_id = s.s_id) '平均分' FROM score s ORDER BY '平均分' DESC
18.查询各科成绩最高分、最低分和平均分:以如下形式显示:课程ID,课程name,最高分,最低分,平均分,及格率,中等率,优良率,优秀率
--及格为>=60,中等为:70-80,优良为:80-90,优秀为:>=90 (超级重点)
SELECT c_id '课程ID', (SELECT name FROM course WHERE c_id = s.c_id) '课程名', MAX(score) '最高分', MIN(score) '最低分', AVG(score) '平均分', AVG(IF(score >= 60, 1, 0)) '及格率', AVG(IF(score BETWEEN 70 AND 80, 1, 0)) '中等率', AVG(IF(score BETWEEN 80 AND 90, 1, 0)) '优良率', AVG(IF(score >= 90, 1, 0)) '优秀率' FROM score s GROUP BY c_id**率的算法可以用count(符合)/count(*),也可以用avg的方法
19、按各科成绩进行排序,并显示排名(重点row_number)
SELECT c_id, s_id, (SELECT name FROM student WHERE s_id = s.s_id) name, score, (ROW_NUMBER() OVER(PARTITION BY c_id ORDER BY score DESC)) 'rank' FROM score s题目可以有不同理解,我的理解是按照各科group by course的成绩进行排序order by score desc
20、查询学生的总成绩并进行排名(不重点)
SELECT s_id, (SELECT name FROM student WHERE s_id = s.s_id) name, SUM(score) sum, (ROW_NUMBER() OVER (ORDER BY SUM(score) DESC)) 'rank' FROM score s GROUP BY s_id
21 、查询不同老师所教不同课程平均分从高到低显示(不重点)
SELECT c.t_id, s.c_id, AVG(score) FROM score s JOIN course c USING(c_id) JOIN teacher t USING (t_id) GROUP BY c.t_id ORDER BY AVG(score) DESC
22、查询所有课程的成绩第2名到第3名的学生信息及该课程成绩(重要 25类似)
SELECT * FROM (SELECT sc.c_id, sc.s_id, st.name, score, ROW_NUMBER()OVER(PARTITION BY c_id ORDER BY score DESC) AS rk FROM score sc JOIN student st USING(s_id)) a WHERE rk IN (2,3)这道题确实很重要,在from中应用子查询,将要返回的结果放在from的表格中,用where语句加以控制,选出排名第2和第3的学生信息。注意不要按照课程名,学生信息,rk为2时的成绩,rk为3时的成绩这种思路去做,否则会出现1242的多行返回错误
23、使用分段[100-85],[85-70],[70-60],[<60]来统计各科成绩,分别统计各分数段人数、课程ID和课程名称(重点和18题类似)
SELECT sc.c_id, name, SUM(IF(score >= 85, 1, 0)) '[100-85]', SUM(IF(score BETWEEN 70 AND 84, 1, 0)) '[85-70]', SUM(IF(score BETWEEN 60 AND 69, 1, 0)) '[70-60]', SUM(IF(score < 60, 1, 0)) '[<60]' FROM score sc JOIN course c USING(c_id) GROUP BY sc.c_id这里不要混淆适用sum和count
24、查询学生平均成绩及其名次(同19题,重点)
SELECT s_id, AVG(score), ROW_NUMBER() OVER (ORDER BY AVG(score) DESC) rk FROM score GROUP BY s_id
25、查询各科成绩前三名的记录(不考虑成绩并列情况)(重点 与22题类似)
SELECT * FROM ( SELECT c_id, s_id, score, ROW_NUMBER() OVER (PARTITION BY c_id ORDER BY score DESC) rk FROM score ) a WHERE rk IN (1,2,3)
SELECT c_id, COUNT(*) FROM score GROUP BY c_id
27、查询出只有两门课程的全部学生的学号和姓名(不重点)
SELECT s.s_id, name FROM score s JOIN student USING(s_id) GROUP BY s.s_id HAVING COUNT(*) = 2
28、查询男生、女生人数(不重点)
SELECT gender, COUNT(*) FROM student GROUP BY gender
SELECT (SUM(IF(gender = '男',1,0))) '男生人数', (SUM(IF(gender = '女',1,0))) '女生人数' FROM student
29、 查询名字中含有"风"字的学生信息(不重点)
SELECT * FROM student WHERE name REGEXP '风'
31、查询1990年出生的学生名单(重点year)
SELECT *
FROM student
WHERE YEAR(birth) = 1990
-- WHERE birth LIKE '1990%' -- WHERE birth REGEXP '^1990'
-- WHERE SUBSTRING(birth,1,4) = '1990'
这里多写了几种方法
32、查询平均成绩大于等于85的所有学生的学号、姓名和平均成绩(不重要)
SELECT st.s_id, st.name, AVG(score) FROM student st JOIN score sc USING (s_id) GROUP BY st.s_id HAVING AVG(score) >= 85
33、查询每门课程的平均成绩,结果按平均成绩升序排序,平均成绩相同时,按课程号降序排列(不重要)
SELECT c_id, AVG(score) FROM score GROUP BY c_id ORDER BY AVG(score), c_id DESC
34、查询课程名称为"数学",且分数低于60的学生姓名和分数(不重点)
SELECT st.name, sc.score FROM student st JOIN score sc USING (s_id) JOIN course c USING (c_id) WHERE c.name = '数学' AND score < 60
35、查询所有学生的课程及分数情况(重点)
SELECT st.s_id, st.name, sc.c_id, c.name, score FROM student st JOIN score sc USING (s_id) JOIN course c USING (c_id) ORDER BY s_id, c_id
或者按照真实的排列情况
SELECT st.s_id, st.name, (SELECT score FROM score WHERE c_id = 1 AND s_id = st.s_id)'语文', (SELECT score FROM score WHERE c_id = 2 AND s_id = st.s_id)'数学', (SELECT score FROM score WHERE c_id = 3 AND s_id = st.s_id)'英语' FROM student st
SELECT st.name, c.name, s.score FROM score s JOIN course c USING (c_id) JOIN student st USING (s_id) WHERE score > 70
37、查询不及格的课程并按课程号从大到小排列(不重点)
SELECT * FROM course c JOIN score s USING(c_id) JOIN student st USING(s_id) WHERE score < 60 ORDER BY c_id DESC
38、查询课程编号为03且课程成绩在80分以上的学生的学号和姓名(不重要)
SELECT st.s_id, st.name FROM student st JOIN score s USING(s_id) WHERE c_id = 3 AND score > 80
39、求每门课程的学生人数(不重要,与26题重复)
SELECT c_id, COUNT(*) FROM score GROUP BY c_id
40、查询选修“张三”老师所授课程的学生中成绩最高的学生姓名及其成绩(重要top/limit)
SELECT st.name, s.score FROM student st JOIN score s USING(s_id) JOIN course c USING(c_id) JOIN teacher t USING(t_id) WHERE t.name = '张三' ORDER BY s.score DESC LIMIT 1
MYSQL中用LIMIT 1限制输出行数,SQL Server用SELECT TOP 1限制
41、查询不同课程成绩相同的学生的学生编号、课程编号、学生成绩 (重点)
SELECT DISTINCT a.s_id, a.c_id, a.score FROM score a JOIN score b USING(s_id) WHERE a.score = b.score AND a.c_id != b.c_id题目有歧义,简化理解为“成绩相同”“课程不同”的学生信息作答,自连接很好用
42、查询每门功成绩最好的前两名(同22和25题)
SELECT * FROM (SELECT *, ROW_NUMBER() OVER (PARTITION BY c_id ORDER BY score DESC) rk FROM score) a WHERE rk IN (1, 2)
43、统计每门课程的学生选修人数(超过5人的课程才统计)。要求输出课程号和选修人数,查询结果按人数降序排列,若人数相同,按课程号升序排列(不重要)
SELECT c_id, COUNT(*) '选修人数' FROM score GROUP BY c_id HAVING COUNT(*) > 5 ORDER BY COUNT(*) DESC, c_id
44、检索至少选修两门课程的学生学号(不重要)
SELECT s_id, COUNT(*) FROM score GROUP BY s_id HAVING COUNT(*) >= 2
45、 查询选修了全部课程的学生信息(重点)
SELECT s_id, COUNT(c_id) FROM score GROUP BY s_id HAVING COUNT(c_id) = (SELECT COUNT(*) FROM course)等号右侧内容不可以写成3,也不能写成COUNT(course.c_id)(假设做了JOIN连接)
46、查询各学生的年龄(精确到月份)
SELECT s_id, TimeStampDiff(MONTH, birth, NOW())/12 age FROM studentMySQL中DATEDIFF(大,小)返回正天数;TimeStampDiff(月/日/年,小,大)返回正月/天/年数;SQLserver中DATEDIFF与MySQL的Timestampdiff使用方法一样
47、查询没学过“张三”老师讲授的任一门课程的学生姓名
SELECT st.s_id, st.name FROM student st WHERE st.s_id NOT IN( SELECT s_id FROM student st JOIN score s USING (s_id) JOIN course c USING (c_id) JOIN teacher t USING (t_id) WHERE t.name = '张三')倒推思路,先查出所有张三教过的学生,然后排除掉这些学生,即可
48、查询两门以上不及格课程的同学的学号及其平均成绩
SELECT s_id, AVG(score) FROM score GROUP BY s_id HAVING SUM(IF(score < 60, 1, 0)) > 2
49、查询本月过生日的学生
SELECT * FROM student WHERE MONTH(birth) = MONTH(NOW())
道阻且长,行则将至,下一篇再见!