数据库系统原理课后答案实战用法:范式分解与SQL验证
发布时间:2026/10/12 16:25:06 锦皓数字建站

简介《数据库系统原理与设计》第四版课后答案是一份面向高校数据库课程学习者与备考者的习题解析文档重点解决数据库理论课程中概念易混淆、习题无从下手的问题。它系统讲解了数据、数据库、数据库系统、数据库管理系统的定义与内涵指出数据是描述事物的符号记录数据库是长期储存在电脑内、有组织的、可共享的数据集合并对比了文件系统与数据库系统在共享性、冗余度、数据独立性及整体结构上的差异帮助读者理解DBMS的数据定义、操纵、运行管理等功能以及物理独立性和逻辑独立性的意义。资源包内含1个doc文档约230KB内容覆盖绪论章节核心考点包括数据与其语义的关系、数据库系统的组成、数据库管理系统的功能以及适合文件系统和数据库系统的典型应用举例。已有89人学习/下载适合配合教材逐章巩固、期末复习或考研备考。答案在给出结论的同时补充了简明的解析和示例便于读者快速厘清概念提升对数据库系统原理的掌握程度。1. 这份课后答案不是用来抄的先搞清楚它能帮你省多少事《数据库系统原理与设计》第四版是国内外很多高校计算机和软件工程专业的核心教材配套的课后答案也是一份被反复翻阅的资料。大多数人拿到这份文档的第一反应是“作业不会做了赶紧找答案”但真正把它用出价值的人是用它来对准教材里的概念边界——数据库理论题不像写代码错误往往是概念混淆导致的比如函数依赖和范式分解的方向搞反、事务隔离级别和锁的语义没分清。这份答案的最大作用是给你一条“做完题之后对照检查”的路径而不是替代你思考。适合读这篇文章的人有两类一类是正在上这门课的学生作业和考试隔着几周答案在手但不知道怎么把题目吃透另一类是想系统梳理数据库理论基础的后端开发者平时写增删改查很熟练但一碰到“为什么这个表要拆成两个”就答不上来。接下来我会把这份课后答案拆成几个核心知识块给你一条从“照着做”到“真正会”的路径中间穿插在 MySQL 和 PostgreSQL 里能直接跑的验证方法——这是教材给不了你的部分。2. 范式分解与候选键课后题里的主战场先会找函数依赖再动手拆2.1 拿到一张关系模式第一步不是画图是列全函数依赖《数据库系统原理与设计》第四版课后题里关系模式设计相关的题目占了近三分之一。这些题看起来是“把表拆成第三范式”但绝大多数人做错的原因不在拆表这一步而是开局就把函数依赖列错了漏掉了隐藏依赖或者把传递依赖的方向写反导致后面的每一步都白做。我一般会让学生在草稿纸上先完成三件事第一把这个关系模式里所有属性列出来第二逐条写出题面明确给出的函数依赖第三把隐含的函数依赖通过 Armstrong 公理推出来——比如题面给了 A→B 和 B→C那 A→C 就是隐含依赖不写出来后面必错。做完这三步才允许碰分解。课后答案里那种“直接给分解结果”的写法不适合学习你要做的是先自己在纸上推到同一步再翻开答案对照看偏差出在哪里。2.2 候选键的三种求法属性闭包是最不容易错的笨办法候选键是范式分解的地基求错了后面全乱。常见做法有三种属性闭包法、函数依赖图法、试凑法。我推荐应试时用属性闭包法因为它确定性最高不容易漏。给定一个关系模式 R(U, F)其中 U 是所有属性的集合F 是函数依赖集合。求候选键的步骤是固定的第一步把 U 里的属性分成四类只在 F 左侧出现的属性L类、只在 F 右侧出现的属性R类、两侧都出现LR类、两侧都没出现N类。第二步候选键一定包含所有 L 类和 N 类属性一定不包含 R 类属性。第三步求 L 类 N 类属性集合的闭包如果等于 U那它就是一个候选键如果不等于 U依次把 LR 类属性加进来试每加一个就求一次闭包直到闭包等于 U。比如有个课后题R(A, B, C, D)F {A→B, B→C, C→D, D→B}。L 类属性只有 A因为 A 只在箭头左侧出现。求 A 的闭包A→BB→CC→D所以闭包是 {A, B, C, D}等于全部属性。那么 A 就是唯一候选键。如果题目问的是“有几个候选键”这种题型答案不会是一个需要把 LR 类属性逐个组合试过去答案里通常会给出候选键集合你的推导过程和它一致才算真正做对。2.3 用 SQL 验证函数依赖数据能证伪不能证实这个边界要清楚课后答案只给结果不给验证方法这里就需要自己动手。函数依赖在数据层面是有对应表现的如果 A 的值相同那么 B 的值也必须相同。反过来如果 A 相同但 B 不同这条函数依赖就一定不成立。注意这里的方向是“证伪”而不是“证实”——测试数据有限不能证明依赖在所有情况下成立只能证明某条依赖在约束下是否被破坏。下面这段 SQL 可以拿来验证题目里给出的函数依赖在测试数据集上是否成立-- 假设要验证函数依赖: employee_id - department_name -- 如果同一 employee_id 对应了不同 department_name说明该依赖不成立 SELECT employee_id, COUNT(DISTINCT department_name) AS dept_count FROM employee GROUP BY employee_id HAVING COUNT(DISTINCT department_name) 1;这段查询的思路是按 employee_id 分组统计每个分组内 department_name 的不同取值个数。如果HAVING条件成立也就是某个员工 ID 对应了多个不同的部门名这条函数依赖就在当前数据集上被证伪了。COUNT(DISTINCT ...)是这里的关键函数HAVING负责过滤出异常分组这两个是验证依赖时的标配。但必须注意查询结果为空不代表依赖永远成立只能说当前样本没发现问题。理论的正确性要用定义去证明这是数据库原理课的规矩。2.4 1NF 到 BCNF拆表不是越碎越好保持依赖才是底线课后答案里分解到 3NF 和 BCNF 的题最容易让人困惑的地方在于有时候两个分解方案都对但一个保持了函数依赖另一个没有考试里这就分出了高低。教材第四版对分解有两个硬性要求无损连接性和保持函数依赖。这两个要求有时候冲突——满足 BCNF 的分解不一定能保持所有函数依赖这时候就该退而求其次选 3NF。我经常举的一个例子是R(A, B, C)F {AB→C, C→B}。这个模式上 AB 是候选键但 C→B 里 C 不是超键所以不属于 BCNF。如果强行拆成 R1(A, C) 和 R2(B, C)确实满足 BCNF 了但 AB→C 这个依赖跨在两个关系上无法在分解后的模式里继续被检查这就是依赖丢失。答案里如果给的是 3NF 的中间状态往往是为了保住 AB→C。做题的时候遇到这种情况先检查答案的分解是不是同时满足两个条件如果不满足看看是不是题目明确要求优先保持依赖——这是课后答案里最常见的一个考点分化点。下面这个判断表是给考前突击用的参考不是教材原文是多年学生工作经验的提炼范式判断要点常见错判点1NF属性不可再分把组合属性当成原子属性2NF没有非主属性对候选键的部分依赖候选键是复合的时候容易漏判3NF没有非主属性对候选键的传递依赖分不清“对候选键传递”和“普通多级依赖”BCNF每个决定因素都是超键主属性之间的依赖最容易让人误判3. 关系代数到 SQL课后题的两种表达语言会互译才算真懂3.1 选择和投影先写还是先写连接顺序决定性能也决定正确性关系代数和 SQL 是同一件事的两种说法课后题里经常给出一个关系代数表达式让你写出等价 SQL或者反过来。最容易踩的坑是运算顺序关系代数里的表达式是一个树投影和选择越早做越好——这不只是性能优化在结果正确性上也会有影响尤其在涉及连接时提前投影能减少列的干扰提前选择能减少行数。比如课后题常考的表达式 σ_gpa3.0(π_student_id, name(student ⋈ enrollment))翻译成 SQL 时最自然的顺序是先连接再过滤但关系代数里 σ 和 π 的位置已经指明了先做投影和选择。我在给学生讲的时候会让他们记住一句话SELECT 子句对应 πWHERE 子句对应 σJOIN 对应 ⋈GROUP BY 对应 γ。但这个映射有边界——SQL 的 SELECT 是在分组和聚合之后才执行的和关系代数里的 π 出现在表达式前段位置不一样。答案如果直接给出 SQL你要自己检查一下它的执行顺序是否符合代数表达式的优先级。3.2 课后题里的除法运算用 SQL 写出来就是 NOT EXISTS 套 NOT EXISTS关系代数里的除法是很多人最害怕的运算符教材里用“被除数 ÷ 除数 商”来定义光看定义很难想象它实际解决什么问题——比如“找出选修了全部课程的学生”就是典型除法。课后答案通常直接用代数符号给出结果但你自己在数据库里验证的时候需要把它翻译成等价的 SQL。“选修了全部课程的学生”这个问题在一个学生选课关系 SC(sid, cid) 和课程关系 Course(cid) 下SQL 写法是这样的-- 找出选修了全部课程的学生 SELECT DISTINCT s1.sid FROM SC s1 WHERE NOT EXISTS ( SELECT 1 FROM Course c WHERE NOT EXISTS ( SELECT 1 FROM SC s2 WHERE s2.sid s1.sid AND s2.cid c.cid ) );这段 SQL 是双重否定结构外层NOT EXISTS是在找“不存在这样一门课程”的课程集合内层NOT EXISTS是在判断“该学生没选这门课”。如果对于某个学生存在哪怕一门课他没选内层查询就会返回一行外层NOT EXISTS就会把这个学生过滤掉。双重 NOT EXISTS 的语义就是“没有一门课是我不选的”也就是“我选了所有课”。这个翻译关系代数里除法问题的套路很固定建议直接作为模板记忆。3.3 课堂上学了 GROUP BY 但没学 HAVING 的正确位置答案会直接给你示范课后题里凡是涉及“每个”“各个”“平均”这类词的基本都在考分组聚合。《数据库系统原理与设计》第四版特别爱考的问题场景是“查询平均成绩大于某分的学生”这一类这地方有个固定的坑WHERE 不能作用于聚合结果必须用 HAVING。-- 课后习题常见题查询平均分 90 的学生及其平均分 SELECT sid, AVG(score) AS avg_score FROM SC GROUP BY sid HAVING AVG(score) 90 ORDER BY avg_score DESC;WHERE和HAVING的分工是刚性的WHERE在分组前过滤原始行作用于单条记录HAVING在分组后过滤分组结果作用于聚合函数。这段代码里如果错把HAVING AVG(score) 90改成WHERE AVG(score) 90MySQL 会直接报错因为WHERE子句里不允许出现聚合函数。ORDER BY avg_score DESC是对结果排序DESC是降序这是把分组结果按平均分降序排列的取法。课后答案里涉及 GROUP BY 的题目一般都会用这种结构自己写的时候注意HAVING别漏。3.4 连接查询里的 NULL 陷阱等值匹配不到不等于不存在连接是课后题里最基础的考点但也是翻车重灾区。很多人在 INNER JOIN 上做得熟练一到外连接就会在 NULL 语义上栽跟头。典型题目是“列出所有学生的选课情况包括没选课的学生”——这里必须用 LEFT JOIN因为 INNER JOIN 会把没有选课记录的学生直接丢掉。-- 列出所有学生及其选课信息没选课的学生选课字段显示 NULL SELECT s.sid, s.name, sc.cid, sc.score FROM student s LEFT JOIN SC sc ON s.sid sc.sid ORDER BY s.sid;LEFT JOIN的行为很好记保留左表student的全部行右表SC能匹配上的就填真实值匹配不上的填 NULL。最后的ORDER BY s.sid是让结果按学生学号排序方便人眼核对。注意这里sc.cid和sc.score在没选课的学生那一行会是 NULL这不是数据错误这正是 LEFT JOIN 该有的结果。课后答案里如果出现“显示所有学生的选课情况”这类要求你看到答案用了 INNER JOIN 就要警惕了——答案可能简化处理了需要自己在数据库里验证一下看哪份输出才真正满足题意条件。4. 事务、并发与死锁课后题的结论在真实数据库里未必如你所想4.1 教材里讲的四类隔离级别在 MySQL 里用参数能看到典型差异课后题里事务隔离级别是必考概念READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ、SERIALIZABLE 这四档的差异要背但真正理解它们需要看实际的执行效果。MySQL 默认的隔离级别是 REPEATABLE READPostgreSQL 默认是 READ COMMITTED这是最基础的参数差异。在 MySQL 里可以这样查看和修改会话级隔离级别-- 查看当前会话的隔离级别 SELECT transaction_isolation; -- 修改当前会话隔离级别为 READ UNCOMMITTED SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; -- 修改为 SERIALIZABLE 后再验证行为 SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;transaction_isolation在 MySQL 8.x 里是查看会话隔离级别的方式老版本里对应的是tx_isolation这个在新版里已经不推荐使用了。SET SESSION只对当前会话生效不会影响其他连接这是做实验时最安全的选择。隔离级别改了之后再用两个 MySQL 客户端窗口分别开启事务一个写数据不提交另一个读观察是否能看到未提交的数据——在 READ UNCOMMITTED 下能看到在 READ COMMITTED 和 REPEATABLE READ 下看不到。这就是课后题答案里那堆抽象描述的真实表现。4.2 两段锁协议说能避免死锁但真实并发写入下死锁照样发生这是理论书和实际系统偏差最大的地方。教材上写的是“两段锁协议可以保证可串行化调度”这本身没毛病——它在理论层面是对的。但课后题答案里经常顺带推断“只要遵守两段锁协议就不会死锁”这就把两个不同层面的问题混在一起了。两段锁协议保证的是调度的可串行性跟“会不会发生死锁”是两码事。死锁是多个事务各自持有锁又在等对方释放导致的循环等待跟加锁顺序的冲突有关跟隔离级别无关。我做过一个测试两个事务同时对同一张表先 UPDATE 后 INSERT 相反顺序的数据在 REPEATABLE READ 下死锁发生率非常高。MySQL 检测到死锁后会选一个牺牲者回滚并报Deadlock found when trying to get lock; try restarting transaction。课后题里问“如何避免死锁”答案如果只写“遵守两段锁协议”是不够的。实际工程里常用的做法是约定统一的加锁顺序——比如所有事务都先操作低 ID 的记录再操作高 ID 的记录——或者缩短事务时间拿到锁后尽快提交释放。这个差异需要在做课后题时自己补上教材不会主动告诉你这两者的区分。4.3 可串行化调度的判断前驱图法是最稳的做题工具课后题里给出一组并发事务的调度序列要求判断是否可串行化这种题用前驱图来做是最稳妥的。做法是先画出所有事务节点然后根据冲突操作确定有向边的方向最后检查图里有没有环。无环就等价于某个串行调度有环就不可串行化。冲突操作指的是一对操作满足三个条件来自不同事务、访问同一数据项、至少有一个是写操作。这题的关键是别漏掉“不同事务”这个前提条件——同一事务内的两个操作不管怎么冲突都不算冲突只有当两个不同事务都对同一个数据项操作时才需要建边。举个例子事务 T1 写 A事务 T2 随后读 AT2 的读发生在 T1 的写之后那么有一条从 T1 到 T2 的边。要是后面事务 T2 又写 B而 T1 又读 B 且发生在 T2 写之后那又有一条从 T2 到 T1 的边这时候环出现了调度不可串行化。这个判断方法在后几章习题里反复出现而课后答案往往只给出“可串行化”或“不可串行化”的结论不展开图的过程。自己动手画一遍图比直接看答案有用得多。5. 使用这份课后答案的五个高频坑现象、原因、解决5.1 现象对着答案抄完作业考试成绩反而更差了原因是把答案当成“作业辅助工具”只求交差不求理解。数据库原理的考试题目很少直接照搬教材原题而是把函数依赖集合换几个属性、把关系模式改一改、把并发调度序列重排一下核心考点不变但数据全变了。如果只背答案里的具体分解结果碰到变体题就看不出套路。解决方式是把课后答案当成“校对器”先独立做题不会做的标记出来做完后再翻答案重点不看结果看步骤。答案里如果写了“候选键为 A”你要追问自己“这个 A 是怎么求出来的”用前面讲的闭包法自己推一遍。这样每一道题都过了一遍完整的解题流程考试的时候才有迁移能力。这一步不做答案就是废纸。5.2 现象按答案里的 SQL 语句执行报语法错误或者结果对不上原因是教材和课后答案会在不同数据库系统环境里编写同一个 SQL 在不同数据库里的语法细节有差异。比如LIMIT子句是 MySQL 和 PostgreSQL 支持而在 SQL Server 里用的是TOP字符串拼接在 MySQL 里是CONCAT()函数在 PostgreSQL 里是||操作符。版本差异也是变量MySQL 8.x 和 5.7 在窗口函数支持上就有明显差距——这道题只跟版本有关跟答案本身的质量无关。解决方式是手边至少装一个 MySQL 8.x 或 PostgreSQL 15 环境把答案里的 SQL 逐条跑一遍。跑不过去的先不急着改代码去查当前版本的手册看是该用LIMIT还是TOP该用 反引号还是双引号。环境兼容性问题是数据库从业者的日常早点面对它比考试时才发现要好。5.3 现象书上对同一道题给了另一种分解结果和课后答案不一样不知道该信谁原因是这种题本身就是多解的考试里通常不要求唯一答案而是要求分解结果满足无损连接和保持函数依赖这两条满足就算对。教材正文和课后答案可能采用了不同的分解路线比如有人先拆传递依赖有人先拆部分依赖顺序不同中间步骤就不同但最终都满足范式定义。解决方式是把两种结果都拿去做检验先用自然连接看能否还原出原始关系无损连接再从分解后的关系看能否推出全部原始函数依赖保持依赖。两个条件都满足两个答案就都对。这个“多重判断”的过程比结果本身更有价值——它能帮你建立对范式分解的直觉知道自己拆出来的结果靠不靠谱。碰到这种情况不要只取一个答案了事多推一步就是多学一分。5.4 现象关系代数的符号看不明白σ π ⋈ × 这些字母容易混淆原因是教材上的符号有希腊字母和自定义记号两套体系不同版本的教材和习题集存在记号差异。σ 是选择π 是投影⋈ 是自然连接× 是笛卡尔积有的版本还加入了除法 ÷ 和改名 ρ初次接触时混在一起很容易搞混。答案里如果直接用这些符号刚入门的读者很容易卡住。解决方式是把符号和对应 SQL 关键词做一个映射表我在带学生时也这么要求关系代数符号名称对应 SQL 关键词/概念σ选择WHEREπ投影SELECT 列清单⋈自然连接NATURAL JOIN / JOIN USING×笛卡尔积CROSS JOIN÷除法NOT EXISTS 嵌套结构∪并UNION−差EXCEPTMySQL 不支持用 NOT EXISTSρ重命名AS 别名这张表不需要背每次做题前扫一眼就够。用到哪一组符号就去对应那一行的 SQL 验证它的结果这样符号和语义挂钩就不会再混。5.5 现象在数据库里跑函数依赖的验证查询时结果为空以为依赖一定成立原因前面提过这是“数据能证伪不能证实”原则。测试数据里选课记录不够全或者某张表里数据原本就是规范化过的依赖当然不会被破坏但这不代表任意合法数据下依赖都成立。理论上的函数依赖是一个集合级性质一切数据实例都必须满足而空的结果只说明当前这个实例没出问题。解决方式是在验证函数依赖时同时在两个方向测试正向测“同一决定属性是否对应同一被决定属性”反向再用COUNT(DISTINCT)在有明显重复决定属性的字段上测。更可靠的办法是查表结构的完整性约束——如果表定义里有UNIQUE约束或主键那么由该列决定任何其他列的依赖是确定成立的。只有约束层面的保证才能给出理论级的结论查询结果只能作为辅助参考。这一点在做课后题验证时很重要别被“空结果”误导。6. 把答案变成复习闭环给每一道题贴一张“审核标签”建立快速检索的错题路口到了复习周很多人拿着一整本课后答案从头到尾翻一遍合上书还是记不住。我把这套资料用成了一套“错题排雷系统”不止做对了答案还给每一道题标记了“状态”。这道题我第一次做就做对了、第一次做错了但看懂答案了、看懂了答案仍然不知道当时为什么错、或者完全没想到用这个解法。四类状态各用一个符号标注复习的时候只看第三和第四类。具体做法是给自己建一张表格按教材章节归类章节题号核心考点我的状态需要再验证的式子关系模式设计习题 3.5候选键求法做对了但慢属性闭包法提速关系模式设计习题 3.93NF 分解与依赖保持看答案才懂用 SQL 验证无损连接SQL 与关系代数习题 4.7除法转 NOT EXISTS完全没思路跑一遍选全部课程的 SQL并发控制习题 6.4前驱图判可串行化答案能看懂自己画一遍前驱图这张表里的“需要再验证的式子”是从答案里摘出来的关键 SQL 或关键推断比题号更能帮你回忆考点。复习时不要从头到尾翻答案而是一章一章看表格里标出来的薄弱点。一个长期管用的习惯是每周挑一个下午用同一张表把薄弱项再做一遍做对了就划掉做不出来就回到教材对应小节复习一遍再回来——这个检查循环不需要等到期末考试前才启动它就藏在课后作业的节点里。最深的教训来自我带过的一届学生有人第一次作业就对着答案抄到第三次作业才意识到这样做已经跟不上课堂进度了。此后我每次拿到一门课的课后答案第一件事就是把答案从头到尾先读一遍读的是一道题有几种解法、有哪些变体而不是看标准结果。你可以先用最快速度读完一份答案然后把不确定的题目标出来再用一周时间去数据库里逐条验证。这条路走下来一份课后答案就不再是应付作业的辅助道具而是数据库系统原理这本厚书的“精读地图”。希望帮到你。本文还有配套的精品资源点击获取
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。