☰
数据库系统概论习题答案高效利用:从关系代数到范式分解
2026/10/9 9:26:51 网站建设 项目流程

简介:数据库系统概论(第五版)习题答案覆盖教材第1章绪论至第16章数据仓与联机分析处理技术,对应王珊、萨师煊主编教材的全部章节,面向高校计算机相关专业学生、考研备考者及数据库课程教师。答案对每章核心概念与课后问题作了细致解答,如第1章中对数据、数据库、数据库管理系统、数据库系统等概念给出定义,并结合银行账户交易、电商订单等实例加以解析;同时辨析文件系统与数据库系统的区别与联系,阐述使用数据库系统的优点与适用场景,便于读者理解基础原理并建立整体框架。资源为单个docx文档,压缩包大小34KB,下载后可直接阅读或打印;适合配套教材逐章巩固、考前快速回顾以及教学备课参考,尤其有助于掌握关系数据库、数据库设计、恢复技术与并发控制等重难点。目前已有7544人学习下载,是系统梳理数据库理论知识的实用辅助资料。

1. 这本习题答案为什么值得逐章拆解

很多人拿到《数据库系统概论(第五版)》的习题答案,第一个动作就是对答案:错了就改,对了就翻页。但如果你只做这一步,这本题答案的价值基本被浪费掉了。它真正值钱的地方,在于逼你把每个概念在纸上完整算一遍:关系代数写不出来,说明你没理解连接与投影的作用域;范式分解结果不对,说明你对函数依赖的传递链还是黑匣子;并发调度的优先级画错,说明你对冲突操作的定义根本没建立。这篇内容不教你抄答案,而是把课后题拆成一套可以反复自测、能对应考试考点的复习动作。适合正在备课程期末、准备考研初试,或者想系统补一遍关系型数据库理论基础的开发者。

做这本题答案的前提是“先做题再对答案”,这个顺序不能反。一旦先看了答案,你的大脑会进入记忆模式而不是推演模式,考场上换一个参数、换一个表名,立刻翻车。下面几章我会从通用解法框架、重点题型精讲、高频坑位排查三个角度,把这本题答案变成你手里真正能用的复习工具。

2. 破解课后题的通用解法框架:先定位考点,再选解题路径

2.1 一道课后题的正确打开方式:题型决定解题顺序

拿到任何一道课后题,先别急着写,用十秒钟判断它属于哪一类。这本书的课后题大致可以归成五类:概念简答题、关系代数表达式题、SQL查询题、范式推导题、数据库设计题。每一类的解题顺序完全不一样,混着做很容易把时间耗在无效步骤上。

我一般会按下面的表来判断题目类型和切入路径:

题目类型考点定位推荐解题顺序
概念简答题定义、术语、分类先写关键词,再展开成完整句子
关系代数表达式题选择、投影、连接的作用域先拆查询条件,再排操作顺序
SQL查询题分组、嵌套、连接语义先写逻辑思路,再写语句
范式推导题候选键、函数依赖、分解算法先求候选键,再做范式判定
设计题实体、联系、E-R图合并先画局部E-R图,再合并消除冲突

为什么先定位题型?因为这本书的习题答案里,同一个表结构会被反复使用,但每类题对表结构的处理方式不同。关系代数题关注的是操作顺序,SQL题关注的是语义等值,范式题关注的是依赖关系。你把类型认错了,后面的步骤基本全错。

定位完题型之后,我的做题顺序固定为五步。第一步,遮住参考答案,把题目完整抄在草稿纸上,包括表结构和查询条件;第二步,用红笔圈出所有让你犹豫的地方,这些地方就是你的知识缺口;第三步,不翻教材,凭记忆写完整答案;第四步,打开参考答案逐条对照;第五步,把不一致的地方写进错题记录,并标注考点。这五步里,最容易偷懒的是第五步,很多人对完答案就合上书,导致同一个坑下次继续踩。

注意:习题答案的正确用法是“校对自己的思维过程”,不是核对最终结果。结果对了但过程绕远,考试时一样会超时。

2.2 从题目回看教材:核心章节的知识地图与习题分布

这本教材的课后习题分布是有规律的。很多时候你觉得自己不会做,不是因为题目难,而是因为没把这章的核心考点理清楚。下面这张表是我按章节整理的考点与题型对照,做题前先扫一眼,心里就有底了:

教材章节核心考点高频题型常见失分点
关系数据库关系完整性、关系运算概念简答、关系代数运算闭包与作用域混淆
SQL语言嵌套查询、分组统计SQL书写、改错聚合函数与WHERE混用
关系数据理论函数依赖、范式判定候选键求解、分解部分依赖与传递依赖漏判
数据库设计E-R图设计、逻辑转换设计题联系类型识别错误
恢复与并发日志、两段锁协议简答、调度分析冲突操作漏判

每个章节的习题答案,本质上是这章知识的压缩包。比如第二章的关系代数题,每一道都在反复训练你同一件事:如何把一个自然语言查询翻译成有序的操作序列。答第3章SQL题时,你会发现很多查询可以用子查询、也可以用连接,答案里通常只给一种写法,但考试时往往会要求你写出另一种。所以不能只背答案里的写法,还得追问一句“还有没有等价写法”。

我用这本书带复习时有一条原则:每道做错的题,必须回到教材的对应小节,把定义重新抄一遍。比如你错在“无损连接分解”上,那就回到范式那一节,把分解算法从头看一遍。这个动作叫“错题回挂”,它能让习题答案的价值放大好几倍。只对答案不回挂教材,做一百道题也是原地踏步。

2.3 构建自己的答案版本:先闭卷做,再对照答案修订

这本题答案里有一部分题是“多解题”,比如3NF分解的结果不唯一,不同的分解路径可能得到不同的关系模式。如果你直接背标准答案,考试时一旦题目参数变化,你连从哪下手都不知道。所以我建议你构建一份属于自己的答案版本,而不是把书后答案当成唯一真理。

操作方法是这样的。第一遍闭卷做题时,把每一步推导都写在纸上,哪怕丑、哪怕乱,也要保留痕迹。第二遍对照参考答案时,不要划掉自己的错误过程,而是在旁边用另一种颜色的笔写正确路径,并注明“我当时为什么卡住”。第三遍,把这道题浓缩成一个考点卡片,正面写题目类型,背面写解题入口。

下面是我用过的一个修订记录模板,你可以直接抄到笔记软件里:

题目位置我的答案要点参考答案要点差异原因考点归类
第3章第5题用GROUP BY先分组用子查询先过滤没注意WHERE不能接聚合SQL执行顺序
第6章第8题直接分解成两个关系需要先判断候选键漏掉函数依赖闭包候选键求解

这个模板的价值在于强制你写出差异原因。很多情况下,你会发现自己的答案和标准答案结果一样,但思路完全不同,这时差异原因写“思路不同,结果等价”,也是一种收获。它说明你已经理解了这道题的本质,只是在路径选择上做了不同的取舍。

做完修订之后,把这章节的考点频率统计一下,你会发现有些考点反复出现,比如“候选键求解”“无损连接分解”“SQL分组过滤”。这些高频考点就是你复习时优先投入时间的区域,而不是按章节顺序平均用力。

3. 重点章习题精讲:关系代数、SQL与范式分解的解题步骤

3.1 关系代数表达式:把自然语言查询翻译成操作序列

关系代数题的正确答案,本质上是一组有序的操作序列。很多人在这一步翻车,是因为把自然语言里的每个条件单独翻译成一个小操作,然后随便拼在一起,完全不考虑操作之间的依赖关系。我拿一个最常见的题目结构来说明。

假设有三个关系:Student(Sno, Sname, Ssex, Sage, Sdept),Course(Cno, Cname, Cpno, Ccredit),SC(Sno, Cno, Grade)。题目要求查询“计算机系年龄小于20岁的学生的学号与姓名”。我的解题步骤是:

第一步,找出题目里的所有约束条件,这里是“计算机系”和“年龄小于20岁”,它们都是对Student关系的筛选,可以合并进同一个选择操作。第二步,确定需要输出的列,是Sno和Sname,它们来自Student关系。第三步,先做选择,再做投影,写成下面的形式:

σ(Sdept='CS' AND Sage<20)(Student) π(Sno, Sname)(σ(Sdept='CS' AND Sage<20)(Student))

为什么先选择后投影?因为选择操作会扫描整张表的所有列,投影操作则会裁剪列。如果你先投影掉Sdept和Sage,后面再想按这两个条件筛选,数据已经没了,这就是经典的“投影过早”错误。虽然有些DBMS优化器会自动做等价变换,但手写关系代数时,你按先选择、后投影的顺序写,阅卷时最容易拿全分。

再看一个带连接操作的题目:查询“选修了课程号为C02且成绩大于90分的学生的姓名”。这道题涉及SC与Student两个关系,解题顺序是:

步骤操作说明
1在SC上执行选择筛选Cno='C02' AND Grade>90,缩小参与连接的数据量
2与Student做自然连接连接条件是Sno相等
3在连接结果上投影只输出Sname

这样做的理由是尽可能缩小中间结果。如果你先把两个关系做笛卡尔积再筛选,虽然逻辑上没错,但中间结果会膨胀很多。手写关系代数时,尽管没有执行成本的概念,阅卷老师也默认你按“先缩行、再扩列”的顺序组织表达式。

最后提醒一种常见题型:查询“选修了全部课程的学生学号”。很多人会用连接和分组来做,但正确做法通常是除法运算,或者用“不存在一门课程没被该生选修”的双重否定来表达。这道题在习题答案里出现频率很高,值得专门记一下它的标准写法。

3.2 SQL作业题:嵌套查询与分组统计的切入点

SQL题看的是语义准确性和执行效率意识。书后答案通常给一种标准写法,但实际做题时,我会先写一个逻辑版本,再优化成一个更稳的版本。下面用一道典型的分组统计题说明。

题目:查询“每门课程成绩最高分的学生的学号、课程号和成绩”。很多人的第一反应是先用GROUP BY取最高分,然后再去明细里找对应学生。

SELECT sc.Sno, sc.Cno, sc.Grade FROM SC sc JOIN ( SELECT Cno, MAX(Grade) AS MaxGrade FROM SC GROUP BY Cno ) t ON sc.Cno = t.Cno AND sc.Grade = t.MaxGrade;

这段SQL的逻辑是:内层子查询按Cno分组,算出每门课的最高分;外层查询再通过连接条件把最高分对应的明细行找出来。这里的JOIN连接条件有两个,Cno相等、Grade等于最高分,两个条件缺一不可。如果只连接Cno,会把每门课的所有成绩行都带出来,结果完全不对。

有个老生常谈的坑必须注意:不要把MAX(Grade)写进WHERE子句,比如“WHERE Grade = MAX(Grade)”。SQL标准不允许聚合函数直接出现在WHERE中,因为WHERE是在分组之前执行的,你要求分组后的聚合结果去过滤分组前的行,逻辑上互相矛盾。正确位置是HAVING或者子查询里。

再看一道典型的嵌套查询题:查询“没有选修C01课程的学生姓名”。

SELECT Sname FROM Student WHERE Sno NOT IN ( SELECT Sno FROM SC WHERE Cno = 'C01' );

这个写法思路很直接:先在SC里找出选修了C01的学生学号,再从Student里排除这些人。但这里有一个特别隐蔽的坑:如果SC表里Sno列存在NULL值,NOT IN的返回结果可能为空集,导致整个查询查不到任何学生。这是书中习题答案不会特别标注、但实际执行时最容易踩中的雷。

解决方案是把NOT IN改成NOT EXISTS:

SELECT Sname FROM Student s WHERE NOT EXISTS ( SELECT 1 FROM SC sc WHERE sc.Sno = s.Sno AND sc.Cno = 'C01' );

NOT EXISTS是逐行相关子查询,对NULL的处理更安全,而且语义更清晰。做题时,只要看到NOT IN,我第一反应就是检查子查询列里有没有可能混入NULL。如果题目没有声明该列非空,优先用NOT EXISTS版本,稳妥不丢分。

3.3 范式分解的判定顺序:从函数依赖到无损连接

范式题是这本书里理论性最强的一块,也是习题答案占用篇幅最大的部分。做这类题,顺序比计算重要。我总结了一条固定路线:先求候选键,再做范式判定,最后执行分解并验证。

拿一个经典的习题结构来演示。假设关系模式R(学号, 系名, 系主任, 课程号, 成绩),函数依赖集为{学号→系名, 系名→系主任, (学号,课程号)→成绩}。要求判断R属于几范式,并将其分解到3NF。

第一步,求候选键。候选键要能函数决定所有属性。这里学号能决定系名和系主任,但决定不了课程号和成绩;课程号单独也决定不了其他属性。只有学号和课程号组合在一起,才能推导出全部属性,所以候选键是(学号,课程号)。

第二步,判断范式级别。检查每个非主属性对候选键的依赖方式。系名和系主任只依赖于学号,不依赖于课程号,说明它们对候选键存在部分函数依赖,这直接违反了2NF的要求。因此R只属于1NF,需要分解。

第三步,执行分解。先把存在部分依赖的属性拆出去:R1(学号, 系名, 系主任),R2(学号, 课程号, 成绩)。接着检查R1,R1的候选键是学号,函数依赖中有系名→系主任,系主任不直接依赖于学号,存在传递依赖,所以R1不满足3NF,继续拆成R11(学号, 系名)和R12(系名, 系主任)。R2的关系模式中,所有非主属性都完全依赖候选键,且没有传递依赖,满足3NF。

第四步,验证无损连接。一个简单的验证方式是看分解后的关系之间是否保留了足够的信息用于还原原关系。常见的方法是追踪表法,但做题时可以快速检查:R11和R12通过系名连接可以还原R1,R1和R2通过学号连接可以还原R,因此这个分解是无损的。保持函数依赖方面,原有的三个函数依赖都被保留在某个分解关系中,所以这个分解同时满足无损连接和保持依赖。

范式等级判定核心本题判定结果
1NF属性都是原子值满足
2NF非主属性完全依赖候选键不满足,存在部分依赖
3NF无非主属性传递依赖不满足,R1存在传递依赖
BCNF所有决定因素都是候选键分解后各关系满足

最后补充一个经验:别在开始分解时急于动手,先花两分钟把候选键求对。候选键求错,后面的所有判定全部作废。而候选键求解又依赖函数依赖闭包,所以做题真正的第一步是写出函数依赖集合,并检查有没有遗漏推导,比如“学号→系名”和“系名→系主任”可以推出“学号→系主任”,这个传递依赖就是拆分的依据。

3.4 事务并发题:两段锁协议的可串行化判断

并发控制是期末和考研都喜欢考的小题,它出现在习题答案里时,通常是一串事务的读写操作序列,让你判断是否冲突可串行化。

先记三个冲突操作的定义:不同事务对同一数据项做“写-读”“读-写”“写-写”操作时,如果至少有一个是写,那么它们就是冲突的。“读-读”不冲突,可以并发执行。判断一个调度是否冲突可串行化的办法是画优先图:把每个事务画成一个节点,如果事务T1的某个操作与T2的某个操作冲突,且T1的操作在T2之前,就画一条从T1指向T2的有向边。最后检查图中是否有环,有环则不可串行化,无环则存在拓扑序,调度冲突可串行化。

举个例子,事务T1执行Read(A)、Write(B),事务T2执行Read(B)、Write(A),调度顺序是T1的Read(A)、T2的Read(B)、T1的Write(B)、T2的Write(A)。检查冲突操作:T1的Write(B)与T2的Read(B)冲突,且T1在前,画T1→T2;T2的Write(A)与T1的Read(A)冲突,且T2在前,画T2→T1。图中出现双向环,这个调度不可串行化。

如果题目再追问“是否满足两段锁协议”,那就需要对锁操作做分析。两段锁协议要求每个事务分两个阶段:增长阶段只能加锁不能释放锁,收缩阶段只能释放锁不能再加锁。只要某个事务在释放过任何锁之后又申请新锁,就违反了协议。做题时我给的建议是把每个事务的加锁、释放列成一条时间线,直接在时间线上标出释放位置,然后看释放点之后还有没有LOCK请求。

这一块的计算量不大,但特别容易漏判冲突。我的检查习惯是每个操作对都列出来,逐对判断,不跳步,不要只盯着看起来明显冲突的写写操作,读写和写读同样需要关注。

4. 习题答案自查与避坑:五类常见的翻车现场

4.1 先投影后选择,把条件列弄丢了

现象:写关系代数表达式时,第一步用了投影,第二步再对某个被投影掉的列做选择,结果表达式写了一半就卡住,因为条件列已经不在投影结果里。

原因:投影会移除不需要的列,但后续的筛选需要用到这些列。典型的场景是先π(Sno, Sname),再σ(Sdept='CS'),这时Sdept已经被投影裁剪掉,选择操作无法执行。

解决:记住“先缩行,再缩列”的顺序。任何时候都要先做选择缩小数据范围,再做投影裁剪输出列。如果确实需要在投影后继续筛选,那就把条件列也留在投影结果里,等到最后再裁掉。检查时看一眼表达式里每个操作引用的列是否还存在于当前关系中就行。

4.2 NOT IN 遇到 NULL,查询结果突然变成空表

现象:写“查没选某课的学生”的SQL,用了NOT IN子查询,本地测试时表里恰好有NULL学号,结果查询返回0行,怎么查都是空。

原因:SQL的三值逻辑。当NOT IN子查询的结果集中存在NULL时,外层判断“列不等于子查询结果中的任意值”会与NULL比较,而NULL既不等于也不不等于任何值,最终整体判定为FALSE或UNKNOWN,导致所有行都被过滤。

解决:首选NOT EXISTS写法,相关子查询对NULL的处理更符合直觉;或者先在子查询里显式过滤掉NULL,比如加WHERE Sno IS NOT NULL。以后看到NOT IN条件,第一反应就是查子查询列是否绝对非空,不确定就换写法,这是习题答案里不讲明但考试极爱考的隐含坑。

4.3 无损连接判定追踪表算半天下不对

现象:用追踪表算法验证无损连接时,填了几轮数据项后,不确定某一行算不算“被填满”,最后判断错误。

原因:追踪表法要求初始化时把每个属性标上符号,然后反复扫描函数依赖,若某函数依赖左边属性在表中有多行取值相同,就把右边符号统一。很多人把“行中所有符号都变成a下标”这个条件理解成“有a就行”,导致误判。

解决:死记最终条件:只要最终结果中存在一行全部是a下标(如a1, a2, a3...),就能判定无损连接。做题时我习惯每扫描完一轮函数依赖,就在行末画一条竖线,标记这一轮的快照,防止自己混轮。计算量大的题目就列出符号矩阵逐行写,不要心算,心算必错。

4.4 拿答案对结论却不动手推导,考试换个问法就慌

现象:平时复习时觉得课后题都“看懂”了,因为参考答案一步步都读得明白,但到了模拟考试遇到同考点的新题,完全想不起解法入口。

原因:只读答案进入了“被动理解”模式,大脑会把参考答案当成已知信息,不需要调动主动推理。真正考试没有现成答案,缺少推导路径的肌肉记忆时就断片。

解决:每道题必须闭卷重做,然后把自己写的答案与参考解答逐行对比。对比时不只看结果,还要看第一步切入点是否一致。如果第一步就不一样,说明你的解法路径与标准方法脱轨,要立刻纠偏并复述一遍解题入口是什么。我复习时把“能否不看答案复现完整推导”作为一道题算不算掌握的唯一标准。

4.5 把标准答案当唯一解,遇到多解题目死板抄写

现象:范式分解题,参考答案给出一种分解结果,自己也照抄,但考试时题目要求稍作变化,自己就再也写不出第二种分解。

原因:3NF分解本来就可能存在多种结果,无损连接和保持依赖的约束下,分解路径不唯一。习题答案只展示一种,但不代表其他合法分解错误。

解决:做范式分解题时,每次做完都追问一句“还有没有别的切分方式”。我常用反向验证法:把自己拆出来的关系拿去逐条检查函数依赖是否完整保留、能否无损连接,只要两条都满足,就承认它是合法解。这样既不容易被标准答案框住,考试时也能更灵活。

5. 把答案变成复习资产:自测、错题与考点映射的进阶用法

5.1 按考点频率分配复习资源,而不是按章节平均使力

习题答案统计出来之后,你可以把每道错题的考点汇总成一张表,统计每个考点出现的次数。例如最近一次复习中,“候选键求解”错了3次,“SQL分组过滤”错了2次,“两段锁协议”错了1次。那么下一次复习时间就应该优先投入候选键求解,而不是从头翻第一章重看。这张表就是你的个人考试地图,比目录更真实。

考点错题次数涉及题目类型下次复习优先级
候选键求解3范式推导高
SQL分组过滤2SQL查询高
无损连接验证1范式推导中
关系代数操作顺序1关系代数中

复习节奏上,我习惯按1天、3天、7天的间隔重做错题。第一天重做一遍并对照答案,第三天只看题目不看答案写出完整解法,第七天再做一次,这次要求完全闭卷且复现推导过程。三次都能顺畅完成,才算真正消化。

5.2 反向出题是最快的验证方式

这本题答案用到最后,最高级的技巧是反向出题。把一道“求候选键”的题改成“判断该关系属于第几范式”;把一道“用NOT EXISTS改写了”的题改回“用连接实现”再来一遍。反向出题猛戳知识点的可迁移能力,因为你要站在出题人的角度重新组织条件,这个视角比做题本身更能暴露理解漏洞。

我当年复习到最后阶段,每做错一道题就花十分钟给这道题改一个同类型的变体,比如换一个表名、换一个连接条件、换一个查询条件。能顺利解出变体,才说明原理通了。任何一道题只懂了原题、变体就卡壳,说明你记住的是题目而不是方法。希望这篇梳理能帮你把习题答案用出真正的效果——不是对答案,而是校准思维。希望帮到你。

本文还有配套的精品资源,点击获取

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

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

立即咨询