常见的SQL面试题:经典50例
你是不是一听到"SQL面试题"就有点头皮发麻?别担心,今天我们就来一起抽丝剥茧,把这些看似复杂的题目一一攻克!作为一名工程师,我特别理解大家面对这些题目时的感受------就像拆解一台精密的机器,只要掌握了每个零件的功能,整台机器就能运转自如。
让我们先从最基础的零件开始吧!

SQL基础知识概述
想象一下,SQL就像是我们和数据库对话的"语言"。每一条SQL语句,都是在向数据库发出一个明确的指令。我们来看看这些"指令"都是什么意思:
- SELECT :就像你告诉数据库:"嘿,我想要这些数据!"比如:
[学号, 平均成绩: 组函数avg(成绩)] - FROM :告诉数据从哪儿来,就像去图书馆找书,先要知道在哪个书架。比如:
[涉及到成绩: 成绩表score] - WHERE :设置筛选条件,就像在超市买东西时只挑打折的。比如:
[b.课程号='0003' and b.成绩>80] - GROUP BY :把数据分组,就像把班级里的同学按身高分组。比如:
[每个学生的平均: 按学号分组]- 小提示:在Oracle和SQL Server中,SELECT后面非分组的字段必须出现在GROUP BY后面,但在MySQL中比较灵活。
- HAVING :对分组后的结果加条件,就像筛选出"身高超过1.8米的小组"。比如:
[大于60分] - ORDER BY :排序,就像按成绩高低排座位。比如:
[升序: 成绩 ASC / 降序: 成绩 DESC] - LIMIT :只取前N条记录,就像只拿排行榜前10名。比如:
[limit 2 ==> 从0索引开始读取2个]- 注意:
select * from table limit 2, 1;表示跳过2条后取1条,也就是取第3条记录。
- 注意:
分组函数(Group Functions) 也是我们的好帮手:
- 去重
DISTINCT() - 求和
SUM() - 计数
COUNT() - 平均值
AVG() - 最大值
MAX() - 最小值
MIN()
多表连接:就像拼图一样
当数据分散在多张表里时,我们需要把它们"连接"起来:
- 内连接 (默认是INNER):
JOIN ... ON ...--- 只取两张表都有的数据 - 左连接 :
LEFT JOIN tableName AS b ON a.key = b.key--- 左表全部保留,右表没有的用NULL填充 - 右连接 :
RIGHT JOIN--- 和左连接相反 - UNION:并集(去重)
- UNION ALL:并集(包含重复)
- Oracle专属 :
INTERSECT:交集MINUS (EXCEPT):差集
数据库对象:认识这些"工具"
- 表(Table):存放数据的地方
- 视图(View):存储的SELECT语句,就像给查询结果拍了个快照
- 序列(Sequence):自动生成数字,常用于主键
- 索引(Index):提升查询效率,就像书的目录
- 同义词(Synonym):给表起个别名
视图实战
简单视图:
sql
CREATE VIEW emp_vw AS
SELECT employee_id, last_name, salary
FROM employees
WHERE department_id = 90;
查询视图:
sql
SELECT * FROM emp_vw;
对简单视图可以进行DML操作:
sql
UPDATE emp_vw
SET last_name = 'HelloKitty'
WHERE employee_id = 100;
复杂视图:
sql
CREATE VIEW emp_vw2 AS
SELECT department_id, AVG(salary) avg_sal
FROM employees
GROUP BY department_id;
复杂视图不能进行DML操作,因为它涉及分组函数。
序列实战
sql
CREATE SEQUENCE emp_seq1
START WITH 1
INCREMENT BY 1
MAXVALUE 10000
MINVALUE 1
CYCLE
NOCACHE;
查询当前值和下一个值:
sql
SELECT emp_seq1.currval FROM dual;
SELECT emp_seq1.nextval FROM dual;
索引实战
Oracle会自动为具有唯一约束的列创建索引。我们也可以手动创建:
sql
CREATE INDEX emp_idx ON emp2(name);
CREATE INDEX emp_idx2 ON emp2(id, name);
表的操作:DDL和DML
DDL(数据定义语言):创建、删除、修改表结构
CREATE TABLE ...DROP TABLE ...RENAME ... TO ...TRUNCATE TABLE ...ALTER TABLE ...
DML(数据操纵语言):操作数据
INSERT INTO ... VALUES ...UPDATE ... SET ... WHERE ...DELETE FROM ... WHERE ...
重要查询模板
掌握了这些基础,我们就可以组装出完整的查询语句了:
sql
SELECT ...
GROUP FUNCTIONS (MIN()/MAX()/SUM()/AVG()/COUNT())
FROM ... JOIN ... ON ...
LEFT JOIN ... ON ... RIGHT JOIN ... ON ...
WHERE ...
GROUP BY ...
HAVING ...
ORDER BY ...
ASC / DESC
LIMIT (0,4)
常见SQL面试题:经典50题
现在,让我们进入最激动人心的部分!假设我们有4张表:
- 学生表:student(学号, 学生姓名, 出生年月, 性别)
- 成绩表:score(学号, 课程号, 成绩)
- 课程表:course(课程号, 课程名称, 教师号)
- 教师表:teacher(教师号, 教师姓名)
小建议:动手画一画这几张表的关系图,就像拼乐高前先看说明书,会让后面的工作事半功倍!
一、创建数据库和表
在Navicat中,我们先创建好这些表:
- 学生表:学号设为主键
- 成绩表:学号和课程号设为联合主键,成绩设为FLOAT类型
- 课程表:课程号设为主键
- 教师表:教师号设为主键,教师姓名允许为空
添加数据
sql
-- 学生表
INSERT INTO student(学号, 姓名, 出生日期, 性别) VALUES ('0001', '猴子', '1989-01-01', '男');
INSERT INTO student(学号, 姓名, 出生日期, 性别) VALUES ('0002', '猴子', '1990-12-21', '女');
-- 成绩表
INSERT INTO score(学号, 课程号, 成绩) VALUES ('0001', '0001', 80);
INSERT INTO score(学号, 课程号, 成绩) VALUES ('0002', '0002', 60);
-- 课程表
INSERT INTO course(课程号, 课程名称, 教师号) VALUES ('0001', '语文', '0002');
-- 教师表
INSERT INTO teacher(教师号, 教师姓名) VALUES ('0001', '孟扎扎');
50道面试题分类解析
为了让你更容易消化,我把这50道题分成了几个类别。我们一起来逐个击破!
基础查询类
1. 查询姓"猴"的学生名单
2. 查询姓"孟"的老师人数
sql
SELECT COUNT(教师号) FROM teacher WHERE 教师姓名 LIKE '孟%';
汇总统计分组分析
3. 查询课程编号为"0002"的总成绩
sql
SELECT SUM(成绩) FROM score WHERE 课程号 = '0002';
4. 查询选修该课程的学生人数
sql
SELECT COUNT(DISTINCT 学号) AS 学生人数 FROM score;
5. 查询每门课程的最高和最低分
sql
SELECT 课程号, MAX(成绩) AS 最高分, MIN(成绩) AS 最低分 FROM score GROUP BY 课程号;
6. 查询男生、女生人数
sql
SELECT 性别, COUNT(*) FROM student GROUP BY 性别;
7. 查询平均成绩大于60分的学生学号和平均成绩
sql
SELECT 学号, AVG(成绩) FROM score GROUP BY 学号 HAVING AVG(成绩) > 60;
8. 查询至少选修两门课程的学生学号
sql
SELECT 学号, COUNT(课程号) AS 选修课程数目 FROM score GROUP BY 学号 HAVING COUNT(课程号) >= 2;
9. 查询同名同性学生名单并统计同名人数
sql
SELECT 姓名, COUNT(*) AS 人数 FROM student GROUP BY 姓名 HAVING COUNT(*) >= 2;
10. 查询不及格的课程并按课程号从大到小排列
sql
SELECT 课程号 FROM score WHERE 成绩 < 60 ORDER BY 课程号 DESC;
11. 查询每门课程的平均成绩,并按平均成绩升序排序
sql
SELECT 课程号, AVG(成绩) AS 平均成绩 FROM score GROUP BY 课程号 ORDER BY 平均成绩 ASC;
12. 查询课程编号为"0004"且分数小于60的学生学号
sql
SELECT 学号 FROM score WHERE 课程号 = '0004' AND 成绩 < 60 ORDER BY 成绩 DESC;
13. 统计每门课程的选修人数(超过2人的课程才统计)
sql
SELECT 课程号, COUNT(学号) AS '选修人数' FROM score GROUP BY 课程号 HAVING COUNT(学号) > 2 ORDER BY COUNT(学号) DESC, 课程号 ASC;
14. 查询两门以上不及格课程的同学的学号及其平均成绩
sql
SELECT 学号, AVG(成绩) AS 平均成绩 FROM score WHERE 成绩 < 60 GROUP BY 学号 HAVING COUNT(课程号) >= 2;
15. 查询所有课程成绩小于60分学生的学号、姓名
sql
SELECT 学号, 姓名 FROM student WHERE 学号 IN (SELECT 学号 FROM score WHERE 成绩 < 60);
16. 查询没有学全所有课的学生的学号、姓名
sql
SELECT 学号, 姓名 FROM student WHERE 学号 IN (SELECT 学号 FROM score GROUP BY 学号 HAVING COUNT(课程号) < (SELECT COUNT(课程号) FROM course));
17. 查询只选修了两门课程的全部学生的学号和姓名
sql
SELECT 学号, 姓名 FROM student WHERE 学号 IN (SELECT 学号 FROM score GROUP BY 学号 HAVING COUNT(课程号) = 2);
18. 查询1990年出生的学生名单
sql
SELECT 学号, 姓名 FROM student WHERE YEAR(出生日期) = 1990;
19. 查询各科成绩前两名的记录
sql
SELECT 课程号, MAX(成绩) AS 最大成绩 FROM score GROUP BY 课程号;
20. 查询所有学生的学号、姓名、选课数、总成绩
sql
SELECT a.学号, a.姓名, COUNT(b.课程号) AS 选课数, SUM(b.成绩) AS 总成绩 FROM student AS a LEFT JOIN score AS b ON a.学号 = b.学号 GROUP BY a.学号;
21. 查询平均成绩大于85的所有学生的学号、姓名和平均成绩
sql
SELECT a.学号, a.姓名, AVG(b.成绩) AS 平均成绩 FROM student AS a LEFT JOIN score AS b ON a.学号 = b.学号 GROUP BY a.学号 HAVING AVG(b.成绩) > 85;
22. 查询学生的选课情况:学号,姓名,课程号,课程名称
sql
SELECT a.学号, a.姓名, c.课程号, c.课程名称 FROM student a INNER JOIN score b ON a.学号 = b.学号 INNER JOIN course c ON b.课程号 = c.课程号;
23. 查询出每门课程的及格人数和不及格人数
sql
SELECT 课程号, SUM(CASE WHEN 成绩 >= 60 THEN 1 ELSE 0 END) AS 及格人数, SUM(CASE WHEN 成绩 < 60 THEN 1 ELSE 0 END) AS 不及格人数 FROM score GROUP BY 课程号;
24. 使用分段统计各科成绩,分别统计各分数段人数,课程号和课程名称
sql
SELECT a.课程号, b.课程名称, SUM(CASE WHEN 成绩 BETWEEN 85 AND 100 THEN 1 ELSE 0 END) AS '[100-85]', SUM(CASE WHEN 成绩 >= 70 AND 成绩 < 85 THEN 1 ELSE 0 END) AS '[85-70]', SUM(CASE WHEN 成绩 >= 60 AND 成绩 < 70 THEN 1 ELSE 0 END) AS '[70-60]', SUM(CASE WHEN 成绩 < 60 THEN 1 ELSE 0 END) AS '[<60]' FROM score AS a RIGHT JOIN course AS b ON a.课程号 = b.课程号 GROUP BY a.课程号, b.课程名称;
25. 查询课程编号为0003且课程成绩在80分以上的学生的学号和姓名
sql
SELECT a.学号, a.姓名 FROM student AS a INNER JOIN score AS b ON a.学号 = b.学号 WHERE b.课程号 = '0003' AND b.成绩 > 80;
26. 使用SQL实现将成绩表行转列
sql
SELECT 学号, MAX(CASE 课程号 WHEN '0001' THEN 成绩 ELSE 0 END) AS '课程号0001', MAX(CASE 课程号 WHEN '0002' THEN 成绩 ELSE 0 END) AS '课程号0002', MAX(CASE 课程号 WHEN '0003' THEN 成绩 ELSE 0 END) AS '课程号0003' FROM score GROUP BY 学号;
总结
看到这里,你是不是觉得SQL面试题其实也没那么可怕?其实,常见的面试题主要考察的就是分组取每组最大值、最小值 以及每组最大的N条记录这些核心技能。
就像我们工程师常说的:手中有剑,心里不慌!掌握了这些基础,再遇到类似的题目,你就能像庖丁解牛一样,游刃有余。
如果你觉得这篇文章对你有帮助,不妨花上20分钟,跟着我的思路,把上面的题目亲手写一遍。相信我,当你亲手跑通每一条SQL的时候,那种成就感会让你爱上这个过程的!
加油,未来的SQL大师!🚀
学SQL,光看不练可不行!🔥
超全SQL学习资源来啦:资料+练习+视频一步到位!推荐自学神器👉 SQL自学网 http://xuesql.cn
零基础也能轻松上手,还有入门好站:https://www.w3schools.com
赶紧动起手来,边练边学效率翻倍!🚀