简介:这份《数据库系统概论复习期末试题及答案(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', encoding='utf-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 IN | NOT 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,D,A是候选码 -- 其他属性无法推出全部,所以唯一候选码是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 3NF | 2NF消除部分依赖,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返回UNKNOWN,COUNT(*)和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_wrong=0,做错则 difficulty+1 def update_result(item_id, correct): if correct: cur.execute("UPDATE exam_items SET is_wrong=0 WHERE item_id=?", (item_id,)) else: cur.execute("UPDATE exam_items SET difficulty=MIN(difficulty+1,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(difficulty+1,5)防止难度超过5。参数上,每次自测后调用update_result更新状态,坚持一周就能看到错题集中在哪几章。
进阶用法:把exam_items表按章节导出CSV,用前面的统计脚本重新算占比,对比第一次的分布,如果某章错题占比下降明显,说明复习有效;如果某章错题一直不降,需要回到教材重新看那一章的原理,而不是继续刷题。
我自己的习惯是考前三天不再刷新题,只跑这个自测脚本,把is_wrong=1的题全部过一遍,做对的划掉,做错的在note里补一句“第二次错,原因是XX”。这样上考场前,手里只有一张不断缩短的错题清单,而不是一堆翻不完的PDF。希望帮到你。
本文还有配套的精品资源,点击获取