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.score
2、查询平均成绩大于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_id
    distinct可要可不要
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)
26、查询每门课程被选修的学生数(不重点)
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
36、查询任何一门课程成绩在70分以上的姓名、课程名称和分数(重点)
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 student
    MySQL中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())

两天多的时间,终于写完了所有的题目,本篇完结~
道阻且长,行则将至,下一篇再见!



全部评论

相关推荐

totoroyyw:千年老妖😂
投递华为等公司10个岗位
点赞 评论 收藏
分享
4 8 评论
分享
牛客网
牛客企业服务