简介这份《数据库系统概论复习期末试题及答案(2)》面向高校计算机及相关专业学生用于期末复习与自测帮助梳理数据库课程的核心考点与常见题型。内容覆盖数据库系统基础概念、三级模式与两级映射、关系模型与主键、E-R模型转换、关系规范化与操作异常、事务的ACID特性、并发控制中的共享锁与排他锁、隔离级别及脏读、不可重复读、幻读问题以及数据库恢复与转储冗余数据等知识点并配有单项选择题、填空题等题型及参考答案便于对照检验掌握程度。资源包共1个PDF文件约192KB轻量易读适合打印或移动端随时翻阅。目前已有943人学习下载可作为考前查漏补缺、强化概念理解的实用练习材料。1. 数据库系统概论期末复习从一份PDF试题倒推知识盲区每年期末前总有人把「数据库系统概论复习期末试题及答案」的PDF翻来覆去刷三遍结果一上考场发现选择题里的范式判断、大题里的SQL嵌套和并发调度顺序跟PDF里练的完全不是一回事。问题不在刷题量在于那份试题只给了答案没告诉你答案背后的判定路径。这篇文章要做的是把一份典型复习试题拆成可复现的复习流程先定位它覆盖的章节权重再把每类题型的解题动作标准化最后给出自测和纠错的方法。适合正在准备数据库系统概论期末的本科生也适合想快速捡回关系模型、SQL、范式、事务这几块基础的开发者。下面所有操作都可以用一份本地SQLite或MySQL环境跑通不需要任何特定教材配套资源。2. 先拆试卷结构数据库系统概论期末试题的题型权重与章节映射2.1 从PDF试题反推章节分值分布一份典型的数据库系统概论期末试题结构大致稳定选择题15到20道填空题5到10空简答题3到4道SQL大题2到3道范式与关系代数题1到2道事务与并发题1道。PDF里如果只给了题目和答案没有分值标注可以按题号密度估算SQL和范式部分通常占卷面40%以上事务与并发占15%到20%剩下的分散在绪论、关系模型、完整性约束和存储结构。我一般会先做一张映射表把每道题归到章节再统计各章题量。这样做的目的是避免在绪论这种低分值章节上花太多时间。下面是一段用Python快速统计题号分布的脚本输入是手动整理的题号-章节对照CSV。import csv from collections import Counter # 输入文件两列题号,章节名 # 章节名用教材常见简称绪论/关系模型/SQL/范式/事务/存储 with open(exam_map.csv, encodingutf-8) as f: reader csv.DictReader(f) rows list(reader) chapter_count Counter(r[章节名] for r in rows) total sum(chapter_count.values()) for ch, cnt in chapter_count.most_common(): print(f{ch}: {cnt}题, 占比 {cnt/total:.1%})逻辑说明这段脚本不依赖任何数据库驱动纯标准库。exam_map.csv需要手动整理题号可以只写序号章节名统一用简称避免同一章出现多种写法导致统计分散。参数上如果PDF里题号有跨页重复以实际题目数量为准不要按页码统计。2.2 用SQL把试题知识点建成可查询的表统计完分布后更有效的做法是把每道题的知识点、难度、是否做错建成一张表方便后续按薄弱点筛选。用SQLite建表最轻量不需要额外服务。-- 建表记录每道试题的元信息 CREATE TABLE exam_items ( item_id INTEGER PRIMARY KEY, chapter TEXT NOT NULL, -- 章节 topic TEXT NOT NULL, -- 具体知识点如“第三范式判定” difficulty INTEGER CHECK(difficulty BETWEEN 1 AND 5), is_wrong INTEGER DEFAULT 0, -- 0正确 1错误 note TEXT -- 错因备注 ); -- 插入示例数据 INSERT INTO exam_items (chapter, topic, difficulty, is_wrong, note) VALUES (范式, 第三范式判定, 4, 1, 把传递依赖漏了), (SQL, 嵌套查询EXISTS, 3, 0, ), (事务, 可串行化调度判断, 5, 1, 混淆冲突可串行化与视图可串行化); -- 查询错题按章节和难度排序优先复习高难度错题 SELECT chapter, topic, difficulty, note FROM exam_items WHERE is_wrong 1 ORDER BY difficulty DESC, chapter;逻辑说明difficulty用1到5表示主观评估即可目的是排序而不是精确度量。is_wrong在每次重做后更新这样这张表就是动态的复习清单。参数上note字段建议写具体错因比如“漏了传递依赖”比“范式不熟”有用得多。如果习惯用Excel导出CSV再用上面的Python脚本统计也可以但SQL的筛选和排序更灵活。2.3 按分值权重分配复习时间拿到章节分布后时间分配原则很简单分值占比乘以1.5作为复习时间占比。比如SQL和范式合计占45%就分配约65%的复习时间。绪论如果只占5%花20分钟过一遍核心概念即可不要背细节。这里有个容易翻车的地方很多人按题量而不是分值分配时间。选择题题量多但单题分值低SQL题量少但一道15分。正确做法是先看试卷开头有没有分值说明没有就按常见权重估算SQL大题通常每题10到15分范式题8到12分事务题10分左右。3. 关系模型与SQL大题从试题答案反推可复现的解题步骤3.1 关系代数与SQL的对应关系怎么练数据库系统概论期末试题里关系代数题和SQL题往往成对出现先让你写关系代数表达式再写等价SQL。很多人关系代数能写SQL也能写但两者对应不上原因是没把五种基本运算和SQL子句的映射关系固定下来。映射关系如下表关系代数运算SQL对应注意点选择 σWHERE条件写法要一致NULL处理不同投影 πSELECT去重要用DISTINCT并 ∪UNION自动去重UNION ALL不去重差 −EXCEPT / NOT INNOT IN遇NULL会翻车笛卡尔积 ×CROSS JOIN后面通常跟选择变成连接自然连接 ⋈NATURAL JOIN / JOIN USING列名必须相同练习方法拿试题里一道关系代数题先写出表达式再逐条翻译成SQL最后用SQLite跑一遍验证结果集是否一致。下面是一道典型题的复现。-- 题目查询选修了“数据库”课程且成绩大于80的学生姓名 -- 关系代数π姓名(σ课程名数据库 ∧ 成绩80 (学生 ⋈ 选课 ⋈ 课程)) -- 对应SQL SELECT DISTINCT s.name FROM student s JOIN sc ON s.sno sc.sno JOIN course c ON sc.cno c.cno WHERE c.cname 数据库 AND sc.grade 80;逻辑说明DISTINCT对应投影的去重语义如果关系代数里写了π姓名SQL里就要考虑是否需要DISTINCT。JOIN的顺序不影响结果但影响可读性一般按学生→选课→课程的顺序写。参数上grade 80如果试题写的是“大于80分”不要写成80这是常见丢分点。3.2 嵌套查询与EXISTS的试题套路期末试题里嵌套查询几乎必考尤其是EXISTS和NOT EXISTS。很多答案PDF只给最终SQL不解释为什么用EXISTS而不是IN。核心区别EXISTS只关心子查询有没有返回行不关心返回什么值所以子查询里写SELECT *还是SELECT 1都一样IN需要子查询返回一个值列表遇NULL会出问题。典型题查询没有选修任何课程的学生姓名。-- 方法一NOT EXISTS推荐语义清晰 SELECT s.name FROM student s WHERE NOT EXISTS ( SELECT 1 FROM sc WHERE sc.sno s.sno ); -- 方法二NOT IN注意NULL陷阱 SELECT s.name FROM student s WHERE s.sno NOT IN ( SELECT sno FROM sc WHERE sno IS NOT NULL );逻辑说明NOT EXISTS是相关子查询对外层每一行学生检查sc里有没有对应选课记录。NOT IN必须加WHERE sno IS NOT NULL否则如果sc里有NULL的sno整个NOT IN返回UNKNOWN结果为空。这是试题答案里经常不写但考试会扣分的点。参数上SELECT 1和SELECT *在EXISTS里等价但SELECT 1更明确表达“不关心值”。如果试题要求用NOT IN记得检查子查询列有没有NULL。3.3 SQL大题的手动执行验证法写完SQL后不要只看答案对不对要手动构造几行数据跑一遍。我一般用SQLite内存库几秒钟就能验证。-- 建最小数据集 CREATE TABLE student (sno TEXT, name TEXT); CREATE TABLE sc (sno TEXT, cno TEXT, grade INTEGER); INSERT INTO student VALUES (S1,张三),(S2,李四),(S3,王五); INSERT INTO sc VALUES (S1,C1,85),(S1,C2,90),(S2,C1,70); -- S3没有选课 -- 跑上面的NOT EXISTS查询预期返回王五逻辑说明最小数据集要覆盖边界情况有选课的学生、没选课的学生、成绩刚好在阈值上下的记录。参数上如果试题涉及AVG、COUNT等聚合数据里要有一组能算出不同结果的记录否则验证不出错误。提示SQLite的NULL处理和MySQL、PostgreSQL基本一致但日期函数和字符串函数有差异试题如果涉及日期建议用试题指定的数据库环境验证。4. 范式判定与事务并发试题里最容易丢分的两类题怎么拆4.1 范式判定题的标准化步骤范式题在期末试题里通常给一个关系模式加一组函数依赖让你判断最高属于第几范式或者分解到3NF/BCNF。很多人凭感觉判断结果一考就错。标准化步骤只有四步第一步写出所有候选码。候选码是能唯一标识元组且没有冗余属性的最小属性集。求候选码用属性闭包从每个属性或属性组合出发求闭包看是否包含全部属性。第二步判断是否存在部分函数依赖。如果候选码是复合属性且非主属性只依赖于候选码的一部分就是2NF不满足。第三步判断是否存在传递函数依赖。如果非主属性依赖于另一个非主属性再依赖于候选码就是3NF不满足。第四步判断每个决定因素是否包含码。如果所有函数依赖的决定因素都包含候选码就是BCNF。下面用一道典型试题复现。-- 题目关系模式 R(A,B,C,D)函数依赖 F{A→B, B→C, A→D} -- 判断R最高属于第几范式 -- 第一步求候选码 -- A的闭包A→B, A→D, B→C所以A能推出A,B,C,DA是候选码 -- 其他属性无法推出全部所以唯一候选码是A -- 第二步A是单属性不存在部分依赖满足2NF -- 第三步A→B, B→C存在传递依赖不满足3NF -- 结论R属于2NF逻辑说明求闭包时按函数依赖反复展开直到不再增加属性。参数上如果试题给的依赖里有AB→C这种复合决定因素要检查AB是否包含候选码。常见错误是把“主属性”和“候选码”混为一谈主属性是包含在任一候选码中的属性不是候选码本身。4.2 事务并发调度题的判断方法事务并发题通常给一个调度序列让你判断是否冲突可串行化或者问某个调度是否产生丢失修改、脏读、不可重复读。判断冲突可串行化的方法是画优先图如果两个事务对同一数据项的操作至少有一个是写且它们在调度中顺序出现就画一条边。如果优先图无环就是冲突可串行化。典型试题调度 r1(A) w2(A) r1(B) w2(B)判断是否冲突可串行化。操作序列 T1: r1(A) r1(B) T2: w2(A) w2(B) 冲突分析 r1(A) 和 w2(A) 冲突r1(A)在前画边 T1→T2 r1(B) 和 w2(B) 冲突r1(B)在前画边 T1→T2 优先图T1→T2无环冲突可串行化 等价串行顺序T1, T2逻辑说明冲突的定义是不同事务对同一数据项的操作至少有一个写。r1(A)和w2(A)冲突r1(B)和w2(B)冲突。优先图无环即可串行化。参数上如果调度里有w1(A) w2(A)边是T1→T2如果有w2(A) w1(A)边是T2→T1。两条边方向相反就成环不可串行化。4.3 用表格整理范式与事务的易混点这两类题丢分往往不是因为不会而是因为概念混淆。下面这张表建议考前过一遍。易混点正确理解常见错误2NF vs 3NF2NF消除部分依赖3NF消除传递依赖把传递依赖当成部分依赖冲突可串行化 vs 视图可串行化冲突可串行化一定视图可串行化反之不成立用视图可串行化判断冲突调度丢失修改 vs 脏读丢失修改是写覆盖脏读是读未提交把脏读当成不可重复读候选码 vs 主码候选码可有多个主码选一个认为主码唯一决定范式级别注意范式判定题如果要求分解分解后的模式要同时满足无损连接和保持函数依赖试题答案里经常只写分解结果不验证自己复习时要补上验证步骤。5. 避坑与排查数据库系统概论期末复习的5个血泪教训5.1 只背答案不跑SQL考试换个数就翻车现象PDF试题里的SQL题看答案都能看懂考试时题目条件从“大于80”改成“大于等于80”或者从“选修了数据库”改成“选修了数据库且没选修操作系统”就写不出来。原因看答案是被动识别写SQL是主动构造。被动识别多了会产生“我会了”的错觉实际没有建立从自然语言到SQL子句的映射。解决每道SQL题至少手动改两个条件重写一遍并用SQLite跑通。改条件的方向包括把改成把AND改成OR把EXISTS改成NOT EXISTS把单表改成多表连接。5.2 范式题凭感觉判断不写候选码现象拿到范式题直接看函数依赖觉得“应该属于3NF”结果答案是2NF。原因跳过了求候选码这一步。范式级别依赖候选码不先确定候选码就无法判断部分依赖和传递依赖。解决强制自己先写候选码再写闭包过程最后下结论。即使考试时间紧候选码也要在草稿纸上写出来。练习时可以用上面的SQL表记录每道题的候选码和最终范式级别对比错因。5.3 事务调度题不画优先图靠直觉排序现象调度序列一长就凭感觉说“这个应该可以串行化”结果优先图有环。原因并发调度的冲突关系不是一眼能看出来的尤其是三个以上事务交叉时直觉不可靠。解决所有冲突操作对都列出来逐对画边最后检查有没有环。练习时可以用纸笔画考试时在草稿纸上画简图。如果调度里操作超过10个先按事务分组再逐组分析。5.4 复习资料只刷一份PDF覆盖面不够现象把一份试题刷了三遍考试发现有一道大题考的是PDF里没出现的知识点比如存储过程或触发器。原因单份试题的覆盖面有限尤其是不同教材章节顺序不同PDF可能偏重某几章。解决至少找两份不同来源的试题按章节映射表对比覆盖差异。如果某章在两份试题里都出现重点复习如果只在其中一份出现至少过一遍核心概念。上面的exam_items表可以合并多份试题的数据按章节统计总题量。5.5 忽略NULL和约束的细节选择题丢分现象选择题里问“NOT IN子查询返回NULL时结果是什么”选了“返回所有不匹配的行”实际是“返回空集”。原因对三值逻辑和NULL传播不熟只记住了语法没记住语义。解决把NULL相关的行为单独整理成一张表包括NULL NULL返回UNKNOWNCOUNT(*)和COUNT(列)的区别NOT IN遇NULL返回空集AVG忽略NULL等。每一条都在SQLite里跑一遍验证。6. 用一份自测脚本把复习闭环跑起来最后一章给一个可执行的自测方法把前面的表、SQL验证和错题记录串起来。核心思路是每复习完一章从exam_items表里随机抽5道错题重做做对后更新is_wrong做错则提高difficulty。这样复习重点会自动向薄弱点倾斜。import sqlite3 import random conn sqlite3.connect(review.db) cur conn.cursor() # 随机抽5道错题优先高难度 cur.execute( SELECT item_id, chapter, topic, difficulty, note FROM exam_items WHERE is_wrong 1 ORDER BY difficulty DESC, RANDOM() LIMIT 5 ) items cur.fetchall() for item in items: print(f[{item[1]}] {item[2]} (难度{item[3]}) 错因{item[4]}) # 重做后手动更新做对则 is_wrong0做错则 difficulty1 def update_result(item_id, correct): if correct: cur.execute(UPDATE exam_items SET is_wrong0 WHERE item_id?, (item_id,)) else: cur.execute(UPDATE exam_items SET difficultyMIN(difficulty1,5) WHERE item_id?, (item_id,)) conn.commit() # 示例第1题做对第2题做错 # update_result(items[0][0], True) # update_result(items[1][0], False)逻辑说明ORDER BY difficulty DESC, RANDOM()保证高难度错题优先出现同难度随机打乱。MIN(difficulty1,5)防止难度超过5。参数上每次自测后调用update_result更新状态坚持一周就能看到错题集中在哪几章。进阶用法把exam_items表按章节导出CSV用前面的统计脚本重新算占比对比第一次的分布如果某章错题占比下降明显说明复习有效如果某章错题一直不降需要回到教材重新看那一章的原理而不是继续刷题。我自己的习惯是考前三天不再刷新题只跑这个自测脚本把is_wrong1的题全部过一遍做对的划掉做错的在note里补一句“第二次错原因是XX”。这样上考场前手里只有一张不断缩短的错题清单而不是一堆翻不完的PDF。希望帮到你。本文还有配套的精品资源点击获取