从课后答案到数据库核心机制:关系代数、SQL与完整性约束的推导路径

发布时间:2026/10/22 1:09:42
从课后答案到数据库核心机制:关系代数、SQL与完整性约束的推导路径
简介《数据库系统概论第五版》王珊版课后习题答案文档适合正在学习关系数据库理论的高校学生、考研复习者以及需要系统梳理数据库基础知识的开发人员。文档覆盖第二、三章中关系数据库与结构化查询语言等核心内容对关系模型三个组成部分、关系数据语言分类、实体完整性与参照完整性规则、关系代数五种基本运算及其导出运算、SQL语言五大特点均有清晰解答并给出第二章习题中SPJ数据库的查询语句示例与建表语句可帮助读者对照教材逐题核对、加深理解。内容按章节组织便于按题号快速定位。资源包含1个Word文档整包仅588KB下载后即可直接阅读省去四处搜集答案的时间。目前已有1723人学习使用是备考、考研复习与日常数据库课程学习的实用参考资料。1. 关系模型的课后答案为什么背结论不如推一遍很多人拿到《数据库系统概论》第五版答案第一反应是把简答题背下来、把 SQL 语句抄一遍。但真正让这份答案有分量的不是最终的 SELECT 或 GRANT 语句而是它反复展示的一整套推导动作把自然语言需求翻译成关系代数再把关系代数翻译成 SQL。这个翻译过程才是关系数据库底层工作机制的缩影。关系模型由关系数据结构、关系操作集合和关系完整性约束三部分组成这几乎是书的开篇结论。可绝大多数人只记住了结论没意识到第一部分决定数据怎么组织第二部分决定你能怎么问问题第三部分决定数据能不能允许被问坏。课后答案的价值就在于它给了你一条可复现的推导路径。你可以沿着题目从“需求→关系代数→SQL→约束”走一遍走完以后再去看查询优化器、完整性检查和权限控制都不是黑匣子而是一系列可预期的规则。下面我就按这条路径把这份答案背后的机制拆开讲。2. 关系代数与 SQL两种思维方式的等价换算关系数据语言有三大类关系代数语言、关系演算语言以及兼具两者特点的 SQL。答案里大量出现的关系代数表达式是理解 SQL 的钥匙。关系代数是过程式的你明确告诉系统先做什么、后做什么。SQL 是声明式的你只描述结果的样子具体步骤由优化器决定。两者的等价关系正是从课后习题到真实数据库执行计划的桥梁。2.1 五种基本运算为什么并、差、笛卡尔积、投影、选择是关系代数的公理关系代数的基本运算只有五种并、差、笛卡尔积、投影和选择。其他三种常用运算——交、连接、除——都可以由这五种表达。这个设计不是随意的。并和差来自集合论笛卡尔积能把两个关系的列组合起来投影负责纵向裁剪列选择负责横向裁剪行。五者合在一起刚好覆盖了关系数据操作的全部基本维度行方向、列方向、关系之间的组合与集合运算。看答案里那道经典查询“求供应工程 J1 零件的供应商号码 SNO”π_{Sno}(σ_{JnoJ1}(SPJ))这里的执行顺序是先在 SPJ 表上做选择筛出所有 Jno 等于 J1 的行再做投影只保留 Sno 列。选择先于投影是有代价考量的。如果先投影再选择必须先确定哪些列参与后续判断而选择条件可能依赖被投影掉的列逻辑上就不能成立。实际优化器还会做等价变换比如把选择尽量下推让过滤提前发生减少中间结果体积。这就是为什么课后习题要求你写清楚每一步它不是让你背符号而是让你理解操作顺序会影响最终结果的正确性和性能。五种基本运算的“基本”还体现在它们是完备的。任何关系查询需求都能用这五种的组合表达。答案里“交、连接和除均可用基本运算表达”这句话表面上是数学性质实际上意味着任何面向关系数据的查询语言只要实现了这五种能力就有能力表达一切关系查询。这是关系模型能被 SQL 统一的理论根基。2.2 等值连接与自然连接一个去重动作背后的语义差异答案里有一道题专门问等值连接与自然连接的区别。很多人背了“自然连接是特殊的等值连接要去掉重复列”但没理解这个去重动作到底改变了什么。等值连接是选取 R 与 S 中属性 A、B 值相等的元组结果里两个同值列都会保留。自然连接则要求两个关系中进行比较的分量必须是相同的属性组并且在结果中把重复的属性列去掉。用关系代数表达R ⋈_{AB} S -- 等值连接A 和 B 是不同属性名结果保留两列 R ⋈ S -- 自然连接同名列自动比较结果只保留一列去重不是洁癖。它解决了列语义冗余问题。等值连接里如果 A 和 B 恰好都叫 SNO你会在结果里看到两个 SNO 列一个来自供应商表一个来自供应关系表。自然连接自动识别同名列输出一列让结果更接近“实体”的直觉。但自然连接的隐式条件也有代价如果两个表有多个同名列它会全部拿来比较有时候这不是你想要的。所以设计连接时你得想清楚是追求显式条件等值连接还是追求简洁语义自然连接。答案里那道“求供应工程 J1 红色零件的供应商号码”π_{Sno}( (π_{Sno,Pno}(σ_{JnoJ1}(SPJ)) ) ⋈ (π_{Pno}(σ_{Color红}(P))) )这个写法刻意先投影再做自然连接就是为了让连接只发生在 Pno 一个共同属性上避免把 Sno 或其它列卷进连接条件。这就是自然连接在真实查询里的典型用法用投影“裁剪”出想要的连接键再用自然连接合并。2.3 用 SQL 复写 SPJ 查询非过程化语言如何吃掉关系代数把关系代数翻译成 SQL是整份答案最值钱的部分。看“求供应工程 J1 零件的供应商号码 SNO”的 SQLSELECT DISTINCT SNO FROM SPJ WHERE JNO J1;关系代数里的 σ 变成 WHEREπ 变成 SELECT。这个翻译很直接。但当查询变复杂SQL 会暴露出关系代数里不明显的问题。比如“求没有使用产地 A 供应商生产的红色零件的工程号 JNO”SELECT DISTINCT JNO FROM SPJ WHERE JNO NOT IN ( SELECT SPJ.JNO FROM SPJ, S, P WHERE S.CITY 产地A AND P.COLOR 红 AND S.SNO SPJ.SNO AND P.PNO SPJ.PNO );这里内层子查询先找出所有“用了产地A红色零件”的工程号外层再用 NOT IN 做集合差。SQL 是声明式的你不需要指定是先查子查询还是先扫外层表优化器会决定执行计划。但声明式不代表没有代价如果你没写 DISTINCT多行结果可能重复如果你用 NOT IN 而子查询结果包含 NULL整个查询可能返回空集合。这些坑在后面专门讲。SQL 的高明之处在于综合统一一条语言同时覆盖 DDL、DML、DCL既能交互使用也能嵌入高级语言。答案里强调的“面向集合操作”和“高度非过程化”本质上都是在说你只管描述逻辑别管物理操作。物理操作是优化器的责任而关系代数是你理解优化器行为的唯一工具。2.4 常见误用NOT IN 与 为什么不能互换教材第 3 章习题里有一道很细的题“找出没有使用产地 A 供应商零件的工程号码”。答案专门标注了两种写法-- 写法一适用于 JNO 唯一或唯一性不确定的情况 SELECT DISTINCT JNO FROM SPJ WHERE JNO NOT IN ( SELECT DISTINCT JNO FROM SPJ, S WHERE S.SNO SPJ.SNO AND S.CITY 产地A ); -- 写法二只适用于 JNO 唯一的情况 SELECT DISTINCT JNO FROM SPJ, S WHERE S.SNO SPJ.SNO AND S.CITY 产地A;写法二的逻辑是找到所有“供应商不是产地A”的供应记录然后取工程号。但如果一个工程同时用了产地A和产地B的零件它仍然有一条记录满足“产地B”于是被错误地当成“没用过产地A”。真正“没有使用”是一个集合差概念不是逐行不等于。写法一用 NOT IN 先收集所有产地A相关工程号再从全部工程号中差出去语义才正确。这是一道典型的“现象→原因→解决”现象是结果多出工程号原因是把关系代数里的集合差误写成行级否定解决是优先用 NOT IN 或 NOT EXISTS不要用做集合判断。3. 完整性约束外码为什么允许为空删除时为什么不能一刀切数据库的完整性指的是数据的正确性和相容性。它和安全性的区别很关键安全性防的是非法用户完整性防的是不合语义的数据。答案里把完整性约束条件分成静态列级、静态元组、静态关系、动态列级、动态元组、动态关系六类看起来繁琐实际上是在回答一个问题数据库凭什么知道一条数据“不合理”3.1 三类约束的分工实体完整性约束规定主属性不能取空值。主码是唯一标识一行数据的属性如果它为空这一行就无法定位索引、连接、更新全部失去依据。参照完整性约束规定外码值要么等于被参照表某个元组的主码值要么整体为空。用户定义完整性是最灵活的一层比如年龄不得超过 60 岁工资不能为负数这类规则由具体应用语义决定。三类约束的分工是实体完整性和参照完整性是关系模型自带的骨架用户定义完整性是业务规则。DBMS 的完整性控制机制应具有三个功能定义约束、检查操作请求是否违背约束、发现违约后采取反应。这就是所谓的“定义—检查—违约反应”闭环。你在建表时写 PRIMARY KEY、FOREIGN KEY、CHECK本质上就是在向数据库注册一套自动守门规则。3.2 外码为什么允许为空一个容易答错的例外参照完整性规则里有句绕口的话外部码属性的值可以为空但要有条件。答案给出的条件是若属性 F 本身不是主属性则可以取空值若 F 同时是主属性则不能取空值。怎么理解外码空值表示“这条记录暂时没有关联到目标实体”。比如职工表里的部门号如果允许为空表示该员工还未分配部门这是合法业务状态。但如果外码是主码的一部分比如选课表里 (学号, 课程号) 是联合主码而学号同时是学生表的外码学号就不能为空。因为联合主码要求每个组成部分都非空否则无法保证实体的唯一性。所以外码能不能为空不是外码自己的属性决定的而是它是否同时承担主属性的身份决定的。这个例外经常在面试和考试里出现因为大多数人只记住了“外码可以为空”忽略了“前提是它本身不是主属性”。答案特别强调这一点就是在提醒完整性规则是一组互相制约的约束不能孤立地背一条。3.3 违约处理拒绝、级联、置空删除被参照关系的元组时系统有三种典型做法。级联删除CASCADE删除父表记录时把所有引用它的子表记录一并删除。受限删除RESTRICT如果子表还有引用拒绝删除父表记录。置空值删除NULLIFIES删除父表记录后把子表中外码值置空表示关联失效但子记录保留。用 SQL 定义外码时这个选择是显式的CREATE TABLE DEPT ( Deptno NUMBER(2) PRIMARY KEY, Deptname VARCHAR(10) ); CREATE TABLE EMP ( Empno NUMBER(4) PRIMARY KEY, Ename VARCHAR(10), Age NUMBER(2) CHECK (Age 60), Deptno NUMBER(2), CONSTRAINT FK_DEPTNO FOREIGN KEY (Deptno) REFERENCES DEPT(Deptno) ON DELETE CASCADE );注意这里还隐含了插入规则在参照关系中插入一个新员工时如果他的部门号在 DEPT 中不存在系统默认选择受限插入即拒绝。修改主码的问题更复杂一般不允许用 UPDATE 直接改主码而是先删旧元组再插新元组否则要同时保证主码唯一、非空以及子表外码的连锁变化。3.4 常见问题删除父表记录时到底该选 CASCADE 还是 RESTRICT现象执行 DELETE FROM DEPT WHERE Deptno10数据库直接报外键约束冲突删不掉。原因默认策略是受限删除只要有子表记录引用这个部门数据库就不允许删。解决先确认业务语义。如果部门删除后员工没有存在意义用 ON DELETE CASCADE如果员工需要保留并转为“未分配”用 ON DELETE SET NULL如果历史数据必须封存就限制删除。这个选择本质上是业务完整性策略数据库只是执行工具。很多人把 CASCADE 当成万能解结果删除一个部门连带删掉几千条员工记录这就是没搞懂三级策略的取舍。4. 安全性与视图GRANT/REVOKE 和虚表背后的逻辑数据库安全性是保护数据不被非法使用导致泄露、更改或破坏。安全性问题不是数据库独有但数据库里数据集中存放、多人共享问题被放大了。答案给出的手段里视图机制和权限控制是最容易被低估的两个。4.1 视图机制视图是从一个或几个基本表导出的表它本身不独立存储数据数据库中只放定义。视图在概念上与基本表等同可以在视图上再定义视图。这个“虚表”设计带来四个好处简化用户操作、多角度看待同一数据、对重构数据库提供逻辑独立性、对机密数据提供安全保护。安全保护的典型例子是用户只能看部门平均工资不能看具体个人工资。你可以先建一个视图CREATE VIEW 部门工资 AS SELECT 部门.名称, MAX(工资), MIN(工资), AVG(工资) FROM 职工, 部门 WHERE 职工.部门号 部门.部门号 GROUP BY 部门.名称; GRANT SELECT ON 部门工资 TO 某用户;这个用户对职工表没有任何权限但他可以通过视图查到聚合结果。视图在这里充当了一道强制过滤层底层数据完全不可见暴露的只是经过投影和聚合后的内容。但视图更新有限制。基本表的行列子集视图一般可更新如果视图属性来自集合函数、表达式则肯定不可更新。原因很简单视图是虚表更新最终要转换为基础表的更新而聚合视图无法唯一映射回基础表的行。4.2 DAC 与 MACGRANT/REVOKE 背后是自主存取控制SQL 里的 GRANT 和 REVOKE 实现的是自主存取控制DAC。用户拥有权限后可以用 WITH GRANT OPTION 把自己的权限转授给别人。这带来灵活性也带来风险权限可以在用户之间扩散。强制存取控制MAC则不同系统给每个数据对象标密级给每个用户标许可证级别只有许可证级别达到密级要求才能访问。MAC 是强制性的用户不能改变自己的级别也不能转授权限。看一组典型授权GRANT SELECT, UPDATE(工资) ON 职工 TO 某用户; -- 某用户只能查所有列改“工资”这一列 GRANT ALL PRIVILEGES ON 职工, 部门 TO 管理员 WITH GRANT OPTION; -- 管理员拥有全部权限并且能继续授权给其他人REVOKE 时要注意 CASCADE。如果被回收的用户曾经把权限转授给第三者普通 REVOKE 只能收回他自己的权限第三者权限还在。REVOKE ... CASCADE 会沿着授权链把所有下游权限一并收回。这和级联删除的语义相似安全措施必须能沿着依赖链完整撤销否则会留下权限幽灵。4.3 审计与加密安全体系的最后防线再完善的存取控制也可能被绕过。审计模块把所有操作记入审计日志DBA 可以重现导致数据库当前状况的一系列事件找出非法存取的时间、内容和操作者。数据加密则保护存储和传输中的数据让没有解密算法的人即使拿到数据也无法解读。答案里“审计能重现事件”这个说法是现代数据库安全审计的雏形。你不只需要拦住坏人还要在出事之后有能力还原全过程。安全和完整性在这一点上是相通的两者都依赖系统自动记录和检查而不是事后靠人肉排查。4.4 常见问题GRANT 与 WITH GRANT OPTION 的级联回收现象用 REVOKE 收回了某用户的 SELECT 权限但他授权的另一个用户仍能查询同一张表。原因原用户当初用 WITH GRANT OPTION 把权限转授了出去REVOKE 默认只回收直接权限不会自动回收下游授权。解决回收时显式加 CASCADE让权限回收沿着授权链路传播。以后涉及可转授权限务必先查授权链再决定是单独回收还是级联回收。这个现象特别像删除父表记录时的外键约束你以为只删一条实际上关系链已经把它扩散到了多个位置。5. 避坑关于 NOT IN、范式判断和权限回收的常见翻车记录这一章专门收集课后答案里最容易让人翻车的细节。每一条都是先看到异常结果再追根因最后给解决思路。5.1 NOT IN 遇上 NULL集合差变成全空现象一条 NOT IN 子查询明明有匹配结果外层查询却返回空集。原因SQL 的三值逻辑。子查询结果里只要出现一个 NULLNOT IN的判断会变成“不等于所有值但其中一次比较未知”最终结果未知整行被过滤。解决优先用 NOT EXISTS 改写或者在子查询结果中去掉 NULL。实际开发里NOT IN比想象中危险得多只要被参照的列允许空值就可能踩雷。答案里的练习表主码都非空所以没暴露这个问题真实生产环境可没那么干净。5.2 范式判断部分依赖和传递依赖总是搞混现象判断一个关系模式属于 2NF 还是 3NF答案给出了不同结论自己照着定义套却套错。原因2NF 要求消除非主属性对码的部分函数依赖3NF 要求消除非主属性对码的传递函数依赖。很多人分不清“部分依赖”和“传递依赖”。部分依赖是 X→Y 中仅用 X 的真子集就能决定 Y发生在复合码场景传递依赖是 X→YY→Z且 Y 不函数依赖于 XZ 不是 Y 的子集。解决先找候选码再逐项检查每个非主属性是怎么依赖候选码的。如果依赖只用了候选码的一部分是部分依赖如果需要经过中间属性绕一圈是传递依赖。不要凭直觉判断把函数依赖集写出来一条条对照。5.3 范式判断BCNF 的二目关系证明现象题目说“任何一个二目关系都属于 BCNF”感觉太绝对不敢选正确。原因没有理解 BCNF 的定义。BCNF 规定 X→Y 且 Y 不是 X 的子集时X 必含码。对于一个只有两个属性的关系可能的依赖只有三种A1→A2、A2→A1、两者互相决定或者两者都决定不了对方。前两种情况决定因素就是码第三种情况的码是 (A1,A2)任何决定因素都包含码。所以二目关系必然满足 BCNF。解决遇到范式判断题把关系模式的所有可能依赖列出来而不是举例反驳。举例只能证明存在不能证明必然。5.4 视图更新一用聚合函数就翻车现象建立了一个含 MAX、MIN、AVG 的视图执行 UPDATE 时报错“视图不可更新”。原因视图是虚表更新视图本质是更新基本表聚合视图里的每一行不代表基本表里一个具体元组无法唯一确定要更新哪一行。解决不要在含聚合函数、GROUP BY、表达式的视图上做更新操作。可更新视图只限于行列子集视图也就是直接从一个基本表选出部分行和列且主码完整保留。答案里“基本表的行列子集视图一般是可更新的”这句话就是给这个限制划线。5.5 权限回收WITH GRANT OPTION 造成权限幽灵现象管理员收回了 A 的 INSERT 权限但 B 仍然能插入。原因A 之前把 INSERT 转授给了 BREVOKE 默认不含级联。解决REVOKE 时加 CASCADE并定期用系统目录表检查权限继承关系。这个问题在真实数据库里会导致严重的数据安全事件尤其是离职人员授权的账号没有被回收。答案里专门有 REVOKE ... CASCADE 的习题不是语法练习而是安全运维的必修动作。6. 把课后答案当调试用例从推导路径到设计取舍这份答案文档最有价值的用法不是考前突击而是当作一组可复现的调试用例。每道题都对应一条链路自然语言需求→关系代数→SQL→完整性/安全性设计。这条链路背后是关系模型最核心的设计取舍用集合操作换可优化性。关系代数选择集合级操作意味着你声明“我要什么”优化器负责“怎么做”。代价是 SQL 的某些语义变得反直觉比如 NULL 的三值逻辑、NOT IN 的陷阱、视图更新的限制。这些坑不是偶然的而是集合操作与行级操作之间的语义断层。理解了这一点你在设计真实表结构时就会主动避免可空列、避免聚合视图更新、避免授权链失控。范式化同样是取舍。规范到 BCNF 能消除更新异常但会让查询需要更多连接性能下降。答案里的练习题坚持把关系拆到 BCNF是理论上的纯度真实系统经常保留一定程度的冗余用空间换查询速度。关键是你得先能判断范式等级才能知道自己在牺牲什么。从那以后我每拿到一份数据库习题答案都强制自己先写关系代数、再写 SQL最后检查约束和权限。这个习惯让我少踩了很多 NULL 和级联的坑也让我在排查慢查询时第一反应不再是“加索引”而是先看关系代数等价变换能不能减小中间结果。希望帮到你。本文还有配套的精品资源点击获取