资讯动态

SQL练习题高效刷法:从题型分类到两遍法复盘

发布时间:2026/10/9 9:25:58 来源:尧图企业网站定制
简介《SQL语句练习题及答案》是一份DOC练习题文档面向正在学习数据库原理或SQL语法的高校学生、自学者及备考人员用于夯实建表、增删改查与查询统计等核心技能。内容围绕School数据库中的Student、Course、SC三张表展开系统覆盖建表与主码设置、数据插入与删除、属性修改与更新以及单表查询、统计计算、多表连接、嵌套查询和相关子查询等典型场景。题目条件贴近课堂与考试常见要求如按年龄排序、按10分制显示成绩、计算每门课的平均分与最高分、统计不及格人数、查询平均分前三名等每题附有参考答案便于即时对照。文件共1个DOC文档大小仅43KB轻量易用可直接打开练习或打印。已有521人学习下载无论是课程期末复习、SQL面试准备还是日常练习巩固都能从中获得系统训练。1. 一份 .doc 里的 SQL 练习题为什么比刷一百节视频更值钱搜索“sql语句练习题及答案.doc”这个标题的人多半已经在“看懂教程”和“写出 SQL”之间摔过跟头。跟着视频敲两行当时觉得会了关掉窗口再让写一条带 JOIN 的查询又卡在原地。这个 .doc 文件的价值恰恰不在那几十页纸而在它给了一条能自我验证的路径题目负责把你逼到写不出来答案负责告诉你差在哪。对准备面试、刚转数仓、或者要带新人写数的从业者来说这种“题 答案”的对照训练比刷视频课更贴近真实工作状态。真正的收获不是把答案背下来而是把一道题从读题、拆解到验证的完整过程重复多遍直到形成肌肉记忆。这篇就沿着“先归类题型、再建表练习、最后对照答案复盘”的顺序把每一步怎么落地、坑在哪讲透。2. 拿到练习题先别急着写先看懂这 5 类必考题型新手最容易犯的错误是拿到题就开写写一半发现不会关联、不会分组又回去翻语法。其实练习题翻来覆去就那么几个考点单表查询、多表关联、分组聚合、子查询与 CTE、窗口函数与增删改。先判断题目在考哪一类再决定用什么写法准确率会明显提升。2.1 单表查询一切练习的地基单表查询考察的是 SELECT、WHERE、ORDER BY、LIMIT 这些最基础的子句。它看起来最简单却是后面所有练习的脚手架。大多数人在这一层翻车不是不会写 SELECT而是不知道 WHERE 的执行顺序在 SELECT 之前所以不能在 WHERE 里引用 SELECT 里刚起的别名。练习题最常见的一类是这样的查学生表中某个年份以后出生、成绩大于某个值的学生按年龄排序。-- 单表查询先过滤再投影最后排序 SELECT name, birth_year FROM students WHERE birth_year 2000 AND score 80 ORDER BY birth_year DESC;这段代码的逻辑是先用 WHERE 过滤不满足条件的行再做投影最后排序。两个条件中间用 AND 连接对应题面里的“且”。练习时最容易错的地方是边界值题目写“2000 年以后”到底包不包括 2000 年写“大于 80”那 80 分整算不算动笔前先把这些边界词圈出来写 WHERE 时才能一次到位。单表题的另一个常见变体是分页比如“按成绩从高到低取前 5 名”对应 ORDER BY LIMIT。建议每道题动手前先把题面里的过滤条件圈出来再想 SELECT 要保留哪些列最后才排序和分页。2.2 多表关联JOIN 是区分“背过”和“会写”的分水岭多表关联是面试和工作中真正的分水岭。它考的不只是 LEFT JOIN 和 INNER JOIN 的语法区别还包括关联键有没有重复、会不会造成结果集膨胀、用 ON 过滤和用 WHERE 过滤有什么不同。三道题就能筛选出是“背过语法”还是“真会写”。常见题型是查每个学生的选课信息和对应成绩一张学生表一张选课表一张课程表。-- 三表关联注意关联顺序学生 - 选课 - 课程 SELECT s.name, c.course_name, sc.score FROM students s LEFT JOIN student_courses sc ON s.student_id sc.student_id LEFT JOIN courses c ON sc.course_id c.course_id WHERE sc.score 60 OR sc.score IS NULL;这里 LEFT JOIN 的顺序有讲究先让学生和选课记录关联再用选课记录关联课程一旦把顺序写反比如直接用学生表关联课程表就会产生笛卡尔积结果行数爆炸。ON 后面只放关联条件WHERE 后面才放业务过滤条件这个习惯能帮你避免很多迷糊的报错。练关联题时动笔前先在草稿上画出表之间的关系确认主表和从表再写 JOIN。如果某张表的关联键不唯一结果会出现重复行这也是需要重点检查的地方。2.3 分组与聚合GROUP BY 的边界感在哪里分组聚合是练习题里出错率最高的一块。初学者分不清“分组前过滤”和“分组后过滤”于是把 WHERE 和 HAVING 用反稍微进阶一点的搞不清 SELECT 里哪些列必须出现在 GROUP BY 里跑一条错一条。典型题目统计每门课程的选课人数并筛出选课人数超过 5 人的课程。-- 分组后过滤必须用 HAVING SELECT course_id, COUNT(*) AS student_cnt FROM student_courses GROUP BY course_id HAVING COUNT(*) 5;这里有两个关键点。第一WHERE 在分组前执行HAVING 在分组后执行所以“选课人数超过 5 人”这个条件只能放 HAVING如果题目改成“统计 2020 年以后的选课人数”这个时间条件才放 WHERE。第二SELECT 里出现的非聚合列必须出现在 GROUP BY 里这是标准 SQL 的硬性要求也是练习文档里最爱埋的坑。练分组题时建议把聚合函数圈出来、把分组列写在最前面能少走很多弯路。分组题的变体是“每个班级每门课的平均分”这就是多列分组GROUP BY 后跟两个字段即可。2.4 子查询与 CTE把复杂问题拆成小问题的两种姿势复杂题往往不是考一个技巧而是考多个条件嵌套。子查询和 CTE 就是用来拆解复杂问题的工具。它们的区别在于子查询直接嵌在 WHERE 或 FROM 里CTE 则先声明一段临时结果集再复用。后者在可读性和排错上更友好。典型题查出成绩高于各科平均分的学生名单。难点在于“每科平均分”要先算出来再拿每一条成绩去比一步写成很困难。-- CTE 先把平均分算出来再 JOIN 主查询 WITH avg_scores AS ( SELECT course_id, AVG(score) AS avg_score FROM scores GROUP BY course_id ) SELECT s.name, sc.course_id, sc.score FROM student_courses sc JOIN students s ON s.student_id sc.student_id JOIN avg_scores a ON a.course_id sc.course_id WHERE sc.score a.avg_score;CTE 的作用是“把算平均分”这个子问题独立出来后面直接 JOIN 这段临时结果集每一段都能单独跑通验证。练题时如果一段 SQL 超过二十行优先考虑拆 CTE而不是在一层查询里堆条件。这类题的另一种常见写法是相关子查询在 WHERE 里对每一行重新算一次平均值逻辑一样但可读性和性能都不如 CTE。练习题里凡是出现“比平均、比最大、比最小”这类字眼几乎都能用这个思路解。2.5 窗口函数与更新删除进阶题到底在考什么窗口函数是练习文档里的进阶考点。它和 GROUP BY 最大的区别是GROUP BY 会把多行合并成一行窗口函数则保留每一行同时在行集上做计算。经典题是查每个班级里成绩排名前三的学生。-- 窗口函数先分组排名再过滤前三 SELECT name, class_id, score FROM ( SELECT name, class_id, score, ROW_NUMBER() OVER (PARTITION BY class_id ORDER BY score DESC) AS rn FROM students_scores ) t WHERE rn 3;PARTITION BY 是给窗口划范围相当于每个班级独立排名ORDER BY 决定排名顺序ROW_NUMBER 遇到并列成绩也会给出不同序号。如果题目要求并列名次要换成 RANK 或 DENSE_RANK。练习时只问自己一个问题合并还是不合并。需要保留明细行、又要做排名或累计的用窗口函数只需要汇总结果的用 GROUP BY。绝大多数进阶练习题靠这两类就能覆盖。除查询外文档里通常还会带几道 UPDATE 和 DELETE 题。它们的坑在于很多人忘记先 SELECT 预览影响行数直接执行结果把整张表改了。练习这类题时第一步永远是先写 SELECT 查出将要被影响的行确认无误后再改成 UPDATE 或 DELETE 重新执行。3. 先手写再上机一套能复现的 SQL 练习闭环有了题型认知接下来是“怎么练”的问题。我推荐两遍法先手写再上机验证。很多人打开数据库边写边试结果数据库一次次告诉你答案你自己的推理过程反而没有被训练。正确做法是模拟考试状态手写完成后再逐步上机核验。3.1 准备一套可连续使用的练习环境环境准备只需要三样一个能跑的数据库服务、一个能看结果的客户端、一套能反复重建的样例数据。本地装一个你熟悉的关系型数据库即可开源的商业的都行。重点是建一个专门的练习库和业务库彻底分开不要在核心库上练题这是血泪经验。-- 建练习库按需指定字符集 CREATE DATABASE practice; -- 使用练习库 USE practice;字符集建议选支持中文的 UTF8 系列练习数据里学生姓名、课程名一般都会带中文不配好字符集后面插入数据会出现乱码查错浪费大量时间。建完库之后把每次练习的建表脚本存成单独文件跑挂了就直接重建不用手动清理。这个习惯在做练习题阶段就能避免很多连带事故。3.2 建库建表把题面还原成可信的测试数据练习题文档里的题目通常只有一两句话但很多坑藏在边界条件里比如成绩为空、重复选课、班级人数为零。这时需要自己造数据来验证答案。建表不必一次到位但表结构要能支撑多道题复用。-- 还原题目场景的最小表结构 CREATE TABLE students ( student_id INT PRIMARY KEY, name VARCHAR(50) NOT NULL, birth_year INT, class_id INT ); CREATE TABLE scores ( student_id INT, course_id INT, score DECIMAL(5, 2), PRIMARY KEY (student_id, course_id) );这里把复合主键放在 scores 表上是有意的一个学生同一门课只有一条成绩这能避免练 JOIN 时出现结果集膨胀。如果题目场景允许同一学生补考两次再把主键去掉改成流水号即可。练习时测试数据要比题目给的样例多一倍特别是补上 NULL 值、空字符串、重复记录这几个边界条件决定你的答案经不经得起验证。-- 插入少量可控的测试数据 INSERT INTO students (student_id, name, birth_year, class_id) VALUES (1, 张三, 1999, 101), (2, 李四, 2001, 101), (3, 王五, 2000, 102), (4, 赵六, NULL, 102);注意赵六的出生年故意没填这就是练习里的隐藏边界。凡是题目里可能出现 NULL 的情况你的答案必须明确自己怎么处理它是保留、过滤还是当成默认值。练题前先把这些边界数据造出来等于给标准答案做了一次压力测试很多你以为正确的写法在这组数据上一跑就露馅。3.3 两遍法手写查漏、上机验证完整流程是三步读题、手写、验证。读题时把题目里每个业务词翻译成 SQL 关键词比如“每门课程”翻译成 GROUP BY course_id“高于平均”翻译成 HAVING 或子查询。先在纸上写出关键子句的骨架再补列名和条件。手写阶段不看任何参考资料按考试状态写完整条 SQL。很多人习惯边翻笔记边写这样练的其实是检索能力不是写 SQL 的能力。手写时卡住是好事说明这里有个知识点没内化。把卡住的位置记在题目旁边再带着疑问去查资料记忆深度比直接看笔记高得多。上机验证阶段把自己写的 SQL 和标准答案分别跑一遍不要只比最终结果还要对比两个结果的集合是否完全一致。同一个查询如果两边顺序不同可以先都加上 ORDER BY 再对比。这一步能直接暴露你遗漏的条件或多余的限制。注意结果行数一致也不一定等价还要比较具体列值尤其是 NULL 出现的位置。3.4 用 EXPLAIN 验证答案你以为对了数据库未必这么走练习题往往不要求性能但真实场景要求。因此把答案写对只是第一步学会看执行计划才是区分熟练和初学的重要节点。做法是自己写的查询前面加上 EXPLAIN观察扫描方式和预估行数。EXPLAIN SELECT s.name, sc.score FROM student_courses sc JOIN students s ON s.student_id sc.student_id WHERE sc.course_id 1;输出结果里有几个信息值得关注。扫描类型如果是全表扫描且预估行数很大就该检查 WHERE 列上是不是缺索引关联顺序如果和你的预期相反说明优化器判断另一张表作为驱动表更划算。练习阶段不必追求绝对最优但要能看出明显的低效写法。给 WHERE 列建一个索引再跑一次观察预估行数和扫描类型的变化就能直观感受到索引的作用。练习题的数据量小怎么跑都很快这恰恰会掩盖性能问题。一个实用的习惯是每做完一道题顺手跑一次 EXPLAIN把它当成答案的一部分以后再接触真实业务数据时至少不会两眼一抹黑。4. 标准答案不是用来抄的用参考答案做一次有效复盘练习文档最有价值的部分就是答案。很多人对完答案发现“结果一样”就直接过掉这是极大的浪费。结果一样不等于写法一样更不等于在任何数据集上都等价。一份参考答案的价值是提供一条更简洁、更健壮的思考路径。4.1 答案的三种风格一种写法 vs 多种写法翻开一组练习题答案你会发现同一个题往往有多套写法一套用 IN 子查询一套用 EXISTS还有一套用 JOIN。它们结果等价却对应不同的思维习惯。以“查出没选任何课程的学生”为例-- 风格一NOT IN 子查询 SELECT * FROM students WHERE student_id NOT IN (SELECT student_id FROM student_courses); -- 风格二LEFT JOIN 补空 SELECT s.* FROM students s LEFT JOIN student_courses sc ON s.student_id sc.student_id WHERE sc.student_id IS NULL;风格一直接按题目字面翻译好懂风格二把“没选课”翻译成“关联后没有匹配行”需要多绕一层。两种写法在大多数数据库上结果一致但 NOT IN 遇到子查询结果包含 NULL 时会返回空结果而 LEFT JOIN 版本不受影响。这就是复盘不能只看结果的原因。把你的答案和标准答案的每条子句逐一对照思考作者为什么选这种写法是在规避边界还是在追求可读性。练题时还可以刻意把每道题写两遍先按直觉写再换一种思路写一遍解法视野就是这样打开的。4.2 把标准答案和自己的 SQL 放在一起 diff怎么读差异上机验证时把两边结果都加上 ORDER BY再逐行对比。对比维度至少有四个结果集行数、列名和列顺序、NULL 处理方式、重复行处理方式。结果集行数不同最常见说明两个查询的过滤条件有差异。比如你写了 WHERE score 60标准答案写的是 WHERE score 60而题目给的样例数据里恰好没有 60 分的记录两边结果看不出差别可真实数据里会差出好几行。这就是练习数据要故意构造边界成绩的原因。-- 边界成绩 60 分用同一组数据验证两段查询 SELECT student_id, score FROM scores WHERE score 60; SELECT student_id, score FROM scores WHERE score 60;这样一组对照非常直观。练习题文档的答案通常基于一套规则不会告诉你有没有边界数据当你自己造的测试数据覆盖到 60、NULL、空班级时就能提前发现这种差异。复盘时看到差异不要急着改答案先回题面文字里找依据题目说“大于”60 分就不算题目说“不低于”60 分才算。一切以题面语义为准不以上机结果的“碰巧一致”为准。4.3 从“出结果”到“接近标准”判自己答案的四个维度给自己判分时我习惯用四个维度可读性、健壮性、性能和语法规范。可读性看你是否用了有意义的别名、是否缩进对齐、是否用 CTE 拆分复杂逻辑健壮性看 NULL、重复、空表是否都有明确处理性能看执行计划的扫描类型和预估开销语法规范看是否用了过时写法比如把多条 OR 拼在 WHERE 里而不写 IN。维度检查点反面示例可读性别名清晰、缩进一致a.id、b.id 满天飞健壮性NULL 和重复值有处理直接 NOT IN 不校验 NULL性能执行计划无明显全表扫描大表 WHERE 列无索引规范不写过时语法多个 JOIN 用逗号拼在 WHERE 里这四个维度全部达标一道题才算真正吃透。练习题文档里的标准答案未必四个维度全优但它是你的比较基准复盘时每个维度写一行笔记比抄十遍答案有用得多。如果你发现自己的答案在某个维度上优于参考答案比如可读性更好或性能更优也应该记录原因这能帮你建立自己的评判标准。5. SQL 练习题避坑指南5 个最典型的丢分点与排查方法再把练习中最容易翻车的几个点单拎出来每一类都是实际练习中反复出现的按现象、原因、解决的顺序写方便对号入座。5.1 练习题里最常见的三处“假结果”现象跑出来的结果和标准答案一样但换一组数据就明显不对。原因通常是测试数据过少掩盖了 NULL 和空字符串的差异。比如用 IS NULL 判断一个字段但表里实际存的是空字符串两条记录在界面上看着都是空SQL 处理方式完全不同。解决在测试数据里故意加入 NULL、空字符串、重复记录重新跑两边结果做对比。这一步能提前暴露大多数边界问题。现象WHERE 和 HAVING 混用导致过滤范围错位。具体表现是分组某条件后结果里混进了不该出现的分组。原因是对分组前后执行顺序不敏感把分组后的条件写进了 WHERE而 WHERE 在分组前已经执行完。解决把每个条件翻译成“分组前”还是“分组后”分组后的条件一律放在 HAVING 里。这样一分类基本不会再错。现象关联表后行数暴涨一条学生记录对应出多条相同结果。原因是某个关联键不是唯一键JOIN 时产生了重复匹配。解决先分别查两个表的关联键是否有重复确认唯一后再写 JOIN。练习阶段就把这个校验动作养成习惯真实业务里能少踩不少坑。5.2 同一道题在不同数据库里结果不一致现象一段 SQL 在自己的环境里跑得好好的换另一个数据库结果不同甚至直接报错。原因SQL 方言差异包括字符串截断规则、NULL 排序位置、保留字处理方式。比如某些数据库里 order 是保留字直接当列名用就会报错。解决写练习时先确认题目面向哪类数据库答案尽量贴近标准 SQL。字段名取 order、group、desc 这类保留字时用反引号或双引号包裹或者干脆建表时就换成非保留字省得后面对答案时被语法问题干扰。这一类问题还容易出现在窗口函数的写法上。不同数据库对 ROW_NUMBER 的写法大体一致但 RANK 和 DENSE_RANK 的并列处理略有差异。排查时优先看执行计划或错误提示它们一般会明确指到具体的语法位置。把这类差异记录在练习笔记里比硬背语法更有用。5.3 参考答案本身也有问题现象某道题的标准答案跑出来为空而你自己写的答案有数据反复检查后觉得自己的答案更符合题目要求。原因这类 .doc 练习文档大多是手动整理的版本旧答案没跟上题目改动或录入时出现笔误。解决以题目文字为准答案只作参考。遇到明显可疑的答案先自己造数据验证逻辑验证能说通就以自己的回答为准。拿到文档后先整体扫一遍看有没有“题面和答案不匹配”的迹象别到对答案时才被发现。另一种常见情况是两套写法结果集一致但标准答案的语义和你理解得不一样。比如题目问“未通过的学生”标准答案理解为 fail 字段为 1而你理解为成绩低于 60这两者可能指向不同的人。这时候不要纠结对错回到题面确认业务定义。练习题的目的不是全对而是锻炼自己对业务条件的敏感度这一点在被答案卡住时特别值得坚持。6. 从练题到能答题3 个把练习题吃透的进阶技巧练习题刷完一轮之后很容易陷入“答案都对但换个场景又不会”的循环。我用了三个技巧把练习阶段的成果转成真实可用的能力。6.1 把每道练习题当做一个需求文档来重述动手前先写一句业务说明回答三个问题输入是什么、输出是什么、有哪些边界条件。把这句话以注释形式写在 SQL 上方比如“查每个班级成绩前三的学生成绩并列时都算”。这道题就从一个模糊句子变成了明确的实现方案你会自然想到 SELECT 班级和姓名、按成绩排序、用窗口函数保留明细行。三个问题写不出来时说明题还没读懂这时候不要急着写代码。6.2 反向出题用同一份数据验证两种理解从你造好的测试数据里找特殊值比如 60 分边界、NULL 出生年、重复选课记录先手工推出“这条查询应该返回什么”再写 SQL 验证。如果手推结果和 SQL 输出一致说明这条语句真的在按你的理解执行如果不一致就去查执行顺序或函数细节。这个方法比多做十道新题更有效因为它逼着你解释每一个子句的语义。练习文档里的标准答案只覆盖正确答案不会覆盖你对边界条件的理解反向出题正好补齐这一块。6.3 用三种方式重写同一道题对比差异同一道多表题分别用 JOIN、子查询、窗口函数实现对比结果集和可读性。这个习惯能帮你检验一个关键认知它们各自在什么场景下会失效。我在带人练习时见过不少例子只背 JOIN 的同学遇到“取分组后第一条记录”会愣住而写过窗口函数的人会多一种解法。多写一遍不是重复劳动是在给未来的自己备选方案。最后说句实在话练习文档最重要的不是看完而是“练完”。我自己也经历过对完答案就翻篇的阶段后来发现那些顺手抄下来的答案换到真实需求里根本调不动。真正的收获来自每一次手写卡壳和每一次边界验证。希望这篇能把你的练习路径理顺一点少踩几个我已经踩过的坑祝练题顺利。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑