SQL面试经典50题

   冲浪时发现了知乎上这位老师图解SQL面试题:经典50题 - 知乎总结的五十题,看了下大概可以应付一般的面试场景了,自己做了下,发现了其中有几个答案的问题并进行了改正,记录下方便以后自己面试前翻看。如果大家看到,请配合原文章食用,若发现有什么错误,请不吝赐教。

一、使用Navicat创建数据库和表

分别创建学生表、成绩表、教师表和课程表,其中每个列的约束以及属性都按照原文章的要求创建,并且将数据插入表中。

学生表:

成绩表:

课程表:

教师表:

 

执行插入SQL:

INSERT INTO StuInfo(StuNumber,StuName,BirthDate,StuSex)
VALUES('0001' , '猴子' , '1989-01-01' , '男');
INSERT INTO StuInfo(StuNumber,StuName,BirthDate,StuSex)
VALUES('0002' , '猴子' , '1990-12-21' , '女');
INSERT INTO StuInfo(StuNumber,StuName,BirthDate,StuSex)
VALUES('0003' , '马云' , '1991-12-21' , '男');
INSERT INTO StuInfo(StuNumber,StuName,BirthDate,StuSex)
VALUES('0004' , '王思聪' , '1990-05-20' , '男');


INSERT INTO Score(StuNumber,CourseNumber,Score)
VALUES ('0001' , '0001' , 80);
INSERT INTO Score(StuNumber,CourseNumber,Score)
VALUES ('0001' , '0002' , 90);
INSERT INTO Score(StuNumber,CourseNumber,Score)
VALUES ('0001' , '0003' , 99);
INSERT INTO Score(StuNumber,CourseNumber,Score)
VALUES ('0002' , '0002' , 60);
INSERT INTO Score(StuNumber,CourseNumber,Score)
VALUES ('0002' , '0003' , 80);
INSERT INTO Score(StuNumber,CourseNumber,Score)
VALUES ('0003' , '0001' , 80);
INSERT INTO Score(StuNumber,CourseNumber,Score)
VALUES ('0003' , '0002' , 80);
INSERT INTO Score(StuNumber,CourseNumber,Score)
VALUES ('0003' , '0003' , 80);


INSERT INTO Teacher(TeacherNumber,TeacherName)
VALUES ('0001' , '孟扎扎');
INSERT INTO Teacher(TeacherNumber,TeacherName)
VALUES ('0002' , '马化腾');
INSERT INTO Teacher(TeacherNumber,TeacherName)
VALUES ('0003' , null);
INSERT INTO Teacher(TeacherNumber,TeacherName)
VALUES ('0004' , '');


INSERT INTO Course(CourseNumber,CourseName,TeacherNumber)
VALUES ('0001' , '语文' , '0002');
INSERT INTO Course(CourseNumber,CourseName,TeacherNumber)
VALUES ('0002' , '数学' , '0001');
INSERT INTO Course(CourseNumber,CourseName,TeacherNumber)
VALUES ('0003' , '英语' , '0003');

好了,接下来开始做题!

二、经典50题(重复的几个就不放了)

1.查找姓“猴”的学生名单

SELECT * 
FROM stuinfo
WHERE StuName LIKE '猴%'

/*WHERE StuName LIKE '%猴' 查询以猴结尾的姓名的同学*/
/*WHERE StuName LIKE '%猴%' 查询名字中包含猴的同学*/

其中% 包含零个或多个字符的任意字符串 ,还可以用_(下划线) 任何单个字符。

2.查询课程编号为'0002'的总成绩(需要用到SUM求和函数)

SELECT SUM(score)
FROM Score
WHERE CourseNumber = '0002'

3.查询已经选课的学生人数(可能存在一个同学选了多门课,需要使用Distinct进行去重)

SELECT COUNT(DISTINCT StuNumber) as 选择人数
FROM Score

4.查询各科成绩最高和最低分(需要用到MAX、MIN函数以及分组操作)

SELECT MAX(score) as 最高分,MIN(score) as 最低分
FROM Score
GROUP BY CourseNumber

5.查询每门课被选修的学生人数(首先按照课程号分组,对组内的学号个数进行统计)

SELECT CourseNumber,COUNT(StuNumber)
FROM Score
GROUP BY CourseNumber

6.查询男生、女生人数(依然是分组加count)

SELECT StuSex as 性别,COUNT(StuNumber) as 人数
FROM stuinfo
GROUP BY StuSex

7.查询平均成绩大于60分学生的学号和平均成绩(首先根绝学号进行分组并且查询到平均成绩,这就得到了每个同学的平均成绩,在使用Having条件对分组查询到的结果进行筛选)

SELECT StuNumber,AVG(score)
FROM Score
GROUP BY StuNumber
HAVING AVG(score) > 60;

8.查询至少选修两门课程的学生学号(同上)

SELECT StuNumber
FROM Score
GROUP BY StuNumber
HAVING SUM(CourseNumber) >= 2;

9.查询同名同姓学生名单并统计同名人数(同上)

SELECT StuName,COUNT(StuNumber) as 人数
FROM stuinfo
GROUP BY StuName
HAVING COUNT(StuNumber) >= 2;

10.查询不及格的课程并按课程号从大到小排列(主要使用到了ORDER BY进行排序,DESC为降序,ASC为升序

SELECT CourseNumber
FROM Score
WHERE score < 60
ORDER BY CourseNumber DESC

11.查询每门课程的平均成绩,结果按平均成绩升序排序,平均成绩相同时,按课程号降序排列(首先看到每门课程,所以首先要明确对课程进行分组求平均值,最终按照优先顺序写好排序方式即可)

SELECT AVG(score) as avg_score
FROM Score
GROUP BY CourseNumber
ORDER BY avg_score ASC,CourseNumber DESC

12.检索课程编号为“0004”且分数小于60的学生学号,结果按按分数降序排列(限制where条件即可)

SELECT StuNumber
FROM Score
WHERE CourseNumber='0004' AND score < 60
ORDER BY score DESC

13.统计每门课程的学生选修人数(超过2人的课程才统计),要求输出课程号和选修人数,查询结果按人数降序排序,若人数相同,按课程号升序排序(首先”每门“告诉我们要按照课程号分组,并且求出分组后根据学号统计的数量,最后按照要求排序即可)

SELECT CourseNumber as 课程号,COUNT(StuNumber) as 选修人数
FROM Score
GROUP BY CourseNumber
HAVING COUNT(StuNumber) >= 2
ORDER BY COUNT(StuNumber) DESC , CourseNumber ASC

14.(原博主的答案有误,这里我用的子查询,按照博主的做法及格的成绩查不出来)查询两门以上不及格课程的同学的学号及其平均成绩(首先使用一个查询把不及格两门以上的学号查询出来,这里用到了条件分组,然后外层查询通过IN,在这些学号对应的同学中,在全表中查询学号以及对应的平均成绩

SELECT StuNumber as 学号, AVG(score) as 平均成绩
FROM Score
WHERE StuNumber
IN (
	SELECT StuNumber
	FROM Score
	WHERE score < 60
	GROUP BY StuNumber
	HAVING COUNT(*) >= 2
)
GROUP BY StuNumber;

15.查询学生的总成绩并进行排名(首先按照学号分组,然后按照总成绩降序排名即可)

SELECT StuNumber as 学号, SUM(score) as 总成绩
FROM Score
GROUP BY StuNumber
ORDER BY SUM(score) DESC

16.查询平均成绩大于60分的学生的学号和平均成绩

SELECT StuNumber as 学号, AVG(score) as 平均成绩
FROM Score
GROUP BY StuNumber
HAVING AVG(score) > 60

17.(原博主的答案有误,这里我用的内连接)查询所有课程成绩小于60分学生的学号、姓名(首先将两个表通过学号连接起来,所有符合条件的记录都会拼起来,然后通过学号分组,通过having限制学生成绩的最大值小于60,即可实现所有的分数都不及格)

SELECT stu.StuNumber as 学号,stu.StuName as 姓名
FROM stuinfo stu 
JOIN Score s
ON stu.StuNumber = s.StuNumber
GROUP BY stu.StuNumber
HAVING MAX(s.score) < 60

18.(我觉得这里原文也有问题,应该用NOT IN,如果用IN的话,假设有的学生没有选课的话他就不会被查到)查询没有选修所有课的学生的学号、姓名(先使用一个查询将所有选了全部课的学生的学号查出来,通过对学号分组统计每个学生的课程号的个数可以得到选修课程的总数,再与另一个查询得到的个数比较,最外层使用NOT IN就能得到没有全选的学生了

SELECT StuNumber as 学号,StuName as 姓名
FROM StuInfo
WHERE StuNumber
NOT IN(
	SELECT StuNumber
	FROM score
	GROUP BY StuNumber
	HAVING COUNT(CourseNumber) = (SELECT COUNT(CourseNumber) FROM course)
)

19.查询出只选修了两门课程的全部学生的学号和姓名(只需要对上面的sql稍加修改,NOT IN->IN,并且把等于号后面换成2即可)

SELECT StuNumber as 学号,StuName as 姓名
FROM StuInfo
WHERE StuNumber
IN(
	SELECT StuNumber
	FROM score
	GROUP BY StuNumber
	HAVING COUNT(CourseNumber) = 2
)

常用日期函数 

20.查询出1990年出生的学生名单(所有列)

SELECT * 
FROM stuinfo
WHERE YEAR(BirthDate)=1990

21.查询本月过生日的学生

SELECT * 
FROM stuinfo
WHERE MONTH(BirthDate) = MONTH(CURRENT_DATE())

22.查询所有学生的学号、姓名、选课数、总成绩(注意这里应该使用左连接,否则不选课的学生也会被查出来)

SELECT stuinfo.StuNumber as 学号,StuName,COUNT(CourseNumber) as 选课数,SUM(score) as 总成绩
FROM stuinfo 
LEFT JOIN score
ON stuinfo.StuNumber = score.StuNumber
GROUP BY stuinfo.StuNumber

23.查询平均成绩大于85的所有学生的学号、姓名和平均成绩(这里应该用内连接了,没有选课的学生的成绩不应该被查询出来)

SELECT stuinfo.StuNumber as 学号,StuName as 姓名,AVG(score) as 平均成绩
FROM stuinfo 
JOIN score
ON stuinfo.StuNumber = score.StuNumber
GROUP BY score.StuNumber
HAVING AVG(score)>85

24.查询学生的选课情况:学号,姓名,课程号,课程名称(这里使用两个内连接将三个表连起来就可以了)

SELECT stuinfo.StuNumber as 学号,StuName as 姓名,course.CourseNumber as 课程号,CourseName as 课程名称
FROM stuinfo 
JOIN score
ON stuinfo.StuNumber = score.StuNumber
JOIN course
ON score.CourseNumber = course.CourseNumber

25.查询出每门课程的及格人数和不及格人数(CASE WHEN用来写条件表达式,注意有个坑:不使用group by 并且select后面出现聚合函数的话,那么所有被select的都应该是聚合函数,否则就会报错!)

·讲解CASE WHEN用法的一个博客:Case when的用法_mry6的博客-CSDN博客_case when

SELECT CourseNumber as 课程号, 
SUM(CASE WHEN score >= 60 THEN 1 ELSE 0 END) as 及格人数,
SUM(CASE WHEN score < 60 THEN 1 ELSE 0 END) as 不及格人数
FROM Score

26.使用分段[100-85],[85-70],[70-60],[<60]来统计各科成绩,分别统计:各分数段人数,课程号和课程名称(依旧是使用CASE WHEN)

SELECT c.CourseNumber as 课程号,c.CourseName as 课程名,
SUM(CASE WHEN score BETWEEN 85 AND 100 THEN 1 ELSE 0 END) as '[100-85]',
SUM(CASE WHEN score BETWEEN 70 AND 85 THEN 1 ELSE 0 END) as '[85-70]',
SUM(CASE WHEN score BETWEEN 60 AND 70 THEN 1 ELSE 0 END) as '[70-60]',
SUM(CASE WHEN score< 60  THEN 1 ELSE 0 END) as '[<60]'
FROM course c 
JOIN score s
ON c.CourseNumber = s.CourseNumber
GROUP BY s.CourseNumber,c.CourseName

27.查询课程编号为0003且课程成绩在80分以上的学生的学号和姓名(简单内连接即可)

SELECT stu.StuNumber as 学号, stu.StuName as 姓名
FROM stuinfo stu
INNER JOIN score s
ON s.StuNumber = stu.StuNumber
WHERE s.CourseNumber='0003' AND s.score>80;

28.检索"0001"课程分数小于60,按分数降序排列的学生信息

SELECT stu.*
FROM stuinfo stu
JOIN score s
ON stu.StuNumber = s.StuNumber
WHERE CourseNumber='0001' AND score < 60
ORDER BY score DESC

29.(把原来题干的不同去掉了,不然有些歧义)查询不同老师所教课程平均分从高到低显示(看到老师、课程和分数,首先将三个表通过内连接连接起来,按照教师号分组,原题解按照教师名,但是教师名可能存在重复的。)

SELECT t.TeacherName as 教师姓名, AVG(s.score) as 平均成绩
FROM teacher t 
JOIN course c
ON t.TeacherNumber = c.TeacherNumber
JOIN score s
ON c.CourseNumber = s.CourseNumber
GROUP BY c.TeacherNumber
ORDER BY AVG(score) DESC

30.查询课程名称为"数学",且分数低于60的学生姓名和分数(先将课程、分数以及学生三张表连接起来,通过WHERE限定分数范围和课程范围)

SELECT stu.StuName as 学生姓名,s.score as 成绩
FROM stuinfo stu
JOIN score s
ON stu.StuNumber=s.StuNumber
JOIN course c
ON s.CourseNumber=c.CourseNumber
WHERE c.CourseName='数学' AND s.Score < 60

31.查询任何一门课程成绩在70分以上的姓名、课程名称和分数(同上)

SELECT stu.StuName as 姓名,c.CourseName as 课程名称,s.score as 成绩
FROM stuinfo stu
JOIN score s
ON stu.StuNumber=s.StuNumber
JOIN course c
ON c.CourseNumber=s.CourseNumber
WHERE s.score > 70

32.查询不同课程成绩相同的学生的学生编号、课程编号、学生成绩(这个比较取巧,通过连接自身来查询成绩相同但是课程不相同的, 连接的条件是学生是同一个学生,并且需要去重,否则同一个满足条件的两侧的记录都会查出来,假如现在左侧有a这条记录与右侧b记录满足条件,那么左侧的b与右侧的a也是满足条件的,查出来的记录是一样的,因此需要去重。)

SELECT DISTINCT s1.StuNumber as 学号,s1.score as 成绩,s1.CourseNumber as 课程号
FROM score s1
JOIN score s2
ON s1.StuNumber=s2.stuNumber
WHERE s1.score=s2.score AND s1.CourseNumber != s2.CourseNumber

33.查询课程编号为“0001”的课程比“0002”的课程成绩高的所有学生的学号、姓名(先使用两个子查询把编号为“0001”和“0002”的分别查出来作为两张表,再与学生表连接,通过WHERE限定条件即可)

SELECT s1.StuNumber as 学号,stu.StuName as 姓名
FROM (SELECT stuNumber,score FROM score WHERE CourseNumber='0001') s1
JOIN (SELECT stuNumber,score FROM score WHERE CourseNumber='0002') s2
ON s1.stuNumber = s2.stuNumber
JOIN stuinfo stu 
ON s1.stuNumber = stu.StuNumber
WHERE s1.score > s2.score

34.查询学过编号为“0001”的课程并且也学过编号为“0002”的课程的学生的学号、姓名(把上面的WHERE一删即可)

SELECT s1.StuNumber as 学号,stu.StuName
FROM (SELECT stuNumber,score FROM score WHERE CourseNumber='0001') s1
JOIN (SELECT stuNumber,score FROM score WHERE CourseNumber='0002') s2
ON s1.stuNumber = s2.stuNumber
JOIN stuinfo stu 
ON s1.stuNumber = stu.StuNumber

35.查询学过“孟扎扎”老师所教的所有课的同学的学号、姓名(老师姓名在教师表中,教师表和课程表有关联,课程表与成绩表有关联,成绩表与学生表有关联,所以首先明确将四个表联系起来。可以先将这个老师教过的所有课的课程号查询出来作为子表,在这个表中再查询选了这些课的学生即可)

-----------------直观的写法--------------------
SELECT stu.StuNumber as 学号,stu.StuName as 姓名
FROM stuinfo stu
JOIN score s
ON stu.StuNumber=s.StuNumber
WHERE s.CourseNumber
IN(SELECT c.CourseNumber FROM course c 
	JOIN teacher t ON c.TeacherNumber = t.TeacherNumber
	WHERE t.TeacherName='孟扎扎')

-------------------原写法-----------------------
SELECT st.StuNumber as 学号,st.StuName as 姓名
FROM stuinfo st
JOIN score s
ON st.StuNumber=s.StuNumber
JOIN course c
ON s.CourseNumber=c.CourseNumber
JOIN teacher t
ON t.TeacherNumber=c.TeacherNumber
WHERE t.TeacherName='孟扎扎'

36.查询没学过"孟扎扎"老师课程的学生姓名(将上面改一下,先将该老师开的课程号查出来,再在成绩表中将选过这些课的学生号查出来,外层的学号NOT IN这部分学号就是答案)

SELECT stu.StuNumber as 学号,stu.StuName as 姓名
FROM stuinfo stu
WHERE stu.StuNumber
NOT IN(
	SELECT s.StuNumber
	FROM score s
	WHERE s.CourseNumber
	IN(SELECT c.CourseNumber FROM course c 
	JOIN teacher t ON c.TeacherNumber = t.TeacherNumber
	WHERE t.TeacherName='孟扎扎')
)

37.查询选修“孟扎扎”老师所授课程的学生中成绩最高的学生姓名及其成绩(对35查出来的学生按照成绩排序并使用Limit 1来选取成绩最高的)

SELECT st.StuNumber as 学号,st.StuName as 姓名
FROM stuinfo st
JOIN score s
ON st.StuNumber=s.StuNumber
JOIN course c
ON s.CourseNumber=c.CourseNumber
JOIN teacher t
ON t.TeacherNumber=c.TeacherNumber
WHERE t.TeacherName='孟扎扎'
ORDER BY s.score DESC
LIMIT 1

38.查询至少有一门课与学号为“0001”的学生所学课程相同的学生的学号和姓名(首先用一个子查询把0001这个学生选的课的所有课程号查出来,再从成绩表中查询选过这个结果中的课的学生的学号,最外层再用一次学号查询就可以了。)

SELECT stu.StuNumber as 学号,stu.StuName as 姓名
FROM stuinfo stu
WHERE stu.StuNumber 
IN (
	SELECT DISTINCT(StuNumber) 
	FROM score s
	WHERE s.CourseNumber
	IN(
		SELECT CourseNumber
		FROM score
		WHERE StuNumber='0001'
	)
)
AND stu.StuNumber<>'0001'

39.按平均成绩从高到低显示所有学生的所有课程的成绩以及平均成绩(连接课程和分数两个表,使用CASE WHEN来拿到各科成绩即可)

SELECT StuNumber as 学号,AVG(score),
MAX(CASE WHEN c.CourseName='数学' THEN s.score ELSE NULL END) as '数学',
MAX(CASE WHEN c.CourseName='语文' THEN s.score ELSE NULL END) as '语文',
MAX(CASE WHEN c.CourseName='英语' THEN s.score ELSE NULL END) as '英语'
FROM score s
JOIN course c
ON s.CourseNumber=c.CourseNumber
GROUP BY s.StuNumber
ORDER BY AVG(score)

专用窗口函数部分

窗口函数详解:通俗易懂的学会:SQL窗口函数 - 知乎

窗口函数用法结构:

<窗口函数> over (partition by <用于分组的列名>
                order by <用于排序的列名>)

其中partition子句可是省略,省略就是不指定分组
1) 专用窗口函数,比如rank, dense_rank, row_number等

2) 聚合函数,如sum. avg, count, max, min等

三种排名的窗口函数:

40.查询学生平均成绩及其名次

SELECT StuNumber as 学号,AVG(score),
row_number() over (ORDER BY AVG(score) DESC) as 排名
FROM score 
GROUP BY StuNumber

41.按各科成绩进行排序,并显示排名(通过partition对课程号分组,再按照成绩排序,套上排名的窗口函数即可)

SELECT StuNumber as 学号,score as 成绩,
row_number() over(partition by CourseNumber ORDER BY score) as 排名
FROM score

42.查询每门课程成绩最好的前两名学生姓名(首先通过使用窗口函数的子查询将每门课程选课的学号、成绩以及排名查出来,外层再与学生表连接查询出来Top2)

SELECT StuName,score
FROM(
	SELECT StuNumber,score,
	row_number() over(PARTITION BY CourseNumber ORDER BY score DESC) as ranking
	FROM score
) as tmp
JOIN stuinfo 
ON stuinfo.StuNumber=tmp.StuNumber
WHERE ranking<3

43.查询所有课程的成绩第2名到第3名的学生信息及该课程成绩(同上)

SELECT StuName as 姓名,score as 成绩
FROM(
	SELECT StuNumber,score,
	row_number() over(PARTITION BY CourseNumber ORDER BY score DESC) as ranking
	FROM score
) as tmp
JOIN stuinfo 
ON stuinfo.StuNumber=tmp.StuNumber
WHERE ranking IN(2,3)

44.查询各科成绩前三名的记录(同上)

SELECT StuName as 姓名,score as 成绩
FROM(
	SELECT StuNumber,score,
	row_number() over(PARTITION BY CourseNumber ORDER BY score DESC) as ranking
	FROM score
) as tmp
JOIN stuinfo 
ON stuinfo.StuNumber=tmp.StuNumber
WHERE ranking < 4
  • 7
    点赞
  • 74
    收藏
    觉得还不错? 一键收藏
  • 12
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值