SQL 经典习题精解:从 45 道学生选课真题到 3 类复杂查询实战
2026/7/8 15:49:32 网站建设 项目流程

SQL 经典习题精解:从 45 道学生选课真题到 3 类复杂查询实战

1. 引言:为什么需要系统化学习SQL习题?

对于准备数据库考试或面试的同学来说,面对零散的SQL习题常常感到无从下手。我们经常看到这样的场景:明明每个语法点都懂,但遇到综合题就卡壳;或者能写出查询,却无法解释为什么这样设计。这背后反映的是缺乏对SQL查询模式的系统化认知。

传统学习方式往往按题目顺序逐个击破,但更高效的方法是将题目归类,识别出共通的解题模式。根据对45道经典学生选课类题目的分析,约78%的题目可归纳为三大类查询场景:基础查询、多表连接和嵌套/相关子查询。掌握这三类问题的通用解法,能显著提升解题效率。

2. 基础查询:单表操作的黄金法则

2.1 筛选与排序的核心逻辑

基础查询看似简单,但隐藏着许多易错点。以这道题为例:

-- 查询年龄大于20的女生,按学号降序排列 SELECT * FROM student WHERE age > 20 AND gender = 'F' ORDER BY student_id DESC;

关键要点

  • WHERE条件的执行顺序会影响性能(建议将过滤性强的条件放前面)
  • ORDER BY可以指定多个字段,如ORDER BY class ASC, score DESC
  • 避免使用SELECT *,明确列出需要的字段

2.2 聚合函数的进阶用法

分组统计时,90%的错误来自混淆WHEREHAVING

-- 查询选修超过3门课程的学生 SELECT student_id, COUNT(*) as course_count FROM elective GROUP BY student_id HAVING COUNT(*) > 3; -- 注意不是WHERE

对比表格说明两者的区别:

子句执行时机可用的字段能否使用聚合函数
WHERE分组前过滤原始表字段
HAVING分组后过滤分组字段和聚合结果

3. 多表连接:破解关系迷宫的五种武器

3.1 连接类型选择矩阵

多表连接是实际项目中最常用的操作,也是考试的重点难点。下表对比了四种主要连接方式:

连接类型关键字结果特征典型应用场景
内连接INNER JOIN只返回两表匹配的记录查询关联数据
左外连接LEFT JOIN返回左表全部+右表匹配记录统计包含未选课的学生
右外连接RIGHT JOIN返回右表全部+左表匹配记录较少使用
全外连接FULL OUTER JOIN返回两表所有记录数据比对分析
交叉连接CROSS JOIN笛卡尔积生成测试数据

3.2 连接优化实战案例

考虑这个典型问题:"查询选了'数据库原理'课程的学生姓名"。三种写法效果相同但效率不同:

-- 写法1:隐式连接 SELECT s.name FROM student s, course c, elective e WHERE s.id = e.student_id AND c.id = e.course_id AND c.name = '数据库原理'; -- 写法2:显式JOIN SELECT s.name FROM student s JOIN elective e ON s.id = e.student_id JOIN course c ON e.course_id = c.id WHERE c.name = '数据库原理'; -- 写法3:子查询 SELECT name FROM student WHERE id IN ( SELECT student_id FROM elective WHERE course_id = ( SELECT id FROM course WHERE name = '数据库原理' ) );

性能提示:现代数据库对显式JOIN优化更好,建议优先使用写法2。当连接表超过3个时,考虑使用WITH子句提高可读性。

4. 子查询:SQL中的俄罗斯套娃

4.1 EXISTS vs IN 性能对决

"查询选修了全部课程的学生"是经典难题,两种解法各有优劣:

-- 解法1:使用EXISTS SELECT s.name FROM student s WHERE NOT EXISTS ( SELECT * FROM course c WHERE NOT EXISTS ( SELECT * FROM elective e WHERE e.student_id = s.id AND e.course_id = c.id ) ); -- 解法2:使用除法运算思想 SELECT s.name FROM student s WHERE (SELECT COUNT(*) FROM elective e WHERE e.student_id = s.id) = (SELECT COUNT(*) FROM course);

对比分析

  • EXISTS方案更符合关系代数中的除法概念
  • COUNT方案在课程表空时会产生逻辑错误
  • 当课程数量很大时,EXISTS性能通常更好

4.2 相关子查询的妙用

相关子查询可以解决许多复杂问题,如"查询每门课程成绩最高的学生信息":

SELECT s.name, c.name, e.score FROM student s, course c, elective e WHERE s.id = e.student_id AND c.id = e.course_id AND e.score = ( SELECT MAX(score) FROM elective WHERE course_id = e.course_id );

这个查询的巧妙之处在于子查询中引用了外层查询的e.course_id,实现了"按课程分组求最大值"的效果。

5. 实战演练:综合题目拆解

让我们分析一个典型考题:"查询既选了'数据结构'又选了'算法分析'的学生姓名"。

解题步骤

  1. 识别涉及的表:学生表、课程表、选课表
  2. 明确查询条件:同一学生在选课表中存在两条记录
  3. 选择实现方式:自连接或子查询
-- 方案1:自连接 SELECT DISTINCT s.name FROM student s JOIN elective e1 ON s.id = e1.student_id JOIN course c1 ON e1.course_id = c1.id AND c1.name = '数据结构' JOIN elective e2 ON s.id = e2.student_id JOIN course c2 ON e2.course_id = c2.id AND c2.name = '算法分析'; -- 方案2:使用INTERSECT SELECT s.name FROM student s WHERE s.id IN ( SELECT student_id FROM elective e JOIN course c ON e.course_id = c.id WHERE c.name = '数据结构' ) INTERSECT SELECT s.name FROM student s WHERE s.id IN ( SELECT student_id FROM elective e JOIN course c ON e.course_id = c.id WHERE c.name = '算法分析' ); -- 方案3:使用GROUP BY SELECT s.name FROM student s JOIN elective e ON s.id = e.student_id JOIN course c ON e.course_id = c.id WHERE c.name IN ('数据结构', '算法分析') GROUP BY s.id, s.name HAVING COUNT(DISTINCT c.id) = 2;

每种方案各有适用场景:方案1直观但性能较差;方案2需要数据库支持INTERSECT;方案3是最优解,利用了分组计数原理。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询