简介:这份答案文档对应数据库系统概论(第5版·王珊版)教材,面向高校数据库课程学习者、考研复习者及自学人群。内容按章节编排,覆盖关系模型三要素、关系完整性规则、关系代数五种基本运算,以及SQL语言建表与嵌套查询等核心板块,并针对SPJ数据库典型习题给出符号表达式、SQL语句与解题思路说明,能够帮助读者快速核对作业、巩固关系数据库理论并提升查询书写能力。
包体为1个独立doc文档,整体大小588KB,适配常见办公软件直接打开阅览,便于打印或导入笔记工具。文档结构紧凑,以文字解答为主,适合边学边查,也可作为章节复习提纲。
资源目前已有1723人浏览学习,属于数据库基础领域较常用的配套资料。对正在为王珊版教材习题或考试发愁的读者,这份文档提供了从关系代数到SQL实践的对照参考,可按章节直接用于查漏补缺。
1. 数据库系统概论:一本答案册背后的核心概念地图
很多人第一次翻开《数据库系统概论》的答案册,都会愣住:第 2 章讲关系模型,第 3 章讲 SQL,第 6 章讲范式,第 7 章讲设计流程——每个章节标题都认识,连起来却不知道它们在解决什么问题。其实这门课就是整个数据库世界的“地基课”,它要回答的始终是三个问题:数据怎么组织才不乱、怎么查询才高效、怎么设计才不出错。搞懂这三个问题,不管是应付考试、准备面试,还是以后自己写点带存储的程序,心里都会踏实很多。这本答案册的好处是把每个概念都拆成了“定义 + 例题 + 解答”,照着读就能把零散的知识点串成一张网。接下来我就按这条主线,把这几个核心概念一个个讲透。
2. 关系模型:把现实世界装进一张张表,靠的是三张牌
关系模型是数据库系统概论的第一个大重点。它的核心思想特别朴素:把现实世界里的所有东西都装进一张张二维表里。学生是一张表,课程是一张表,选课记录又是一张表。但你有没有想过,为什么偏偏是“表”这种形式能统治数据库几十年?因为关系模型背后有三张牌在支撑,少了任何一张,这套体系都转不起来。
2.1 三个组成部分:结构、操作、约束,一个都不能少
关系模型的完整定义包含三部分:关系数据结构、关系操作集合、关系完整性约束。通俗地说,就是“长什么样”“能干什么”“有什么规矩”。
关系数据结构就是指那些二维表本身。每张表有行、有列,行叫元组,列叫属性。比如一张“学生”表,学号、姓名、系别就是属性,每一行就是一个具体学生。这张表的骨架就是数据结构。光有骨架还不够,你得能操作它——按条件筛选行、挑出需要的列、把两张表拼起来,这些动作就是关系操作集合。
关系完整性约束则是保证数据不出错的规矩,它分两类:实体完整性和参照完整性。实体完整性说白了就是“主键不能为空”——如果学号是主键,那一行就不能没有学号,否则你没法唯一确定是哪个人。参照完整性呢,就是外键要么为空,要么必须等于另一张表里真实存在的主键值。打个比方,结构是房子的毛坯,操作是你搬进去后的各种行为,约束是物业规定“承重墙不能拆”。三者缺一不可。
2.2 关系代数:五种基本运算搭出所有查询
关系操作的语言有三种:关系代数、关系演算和 SQL。其中关系代数是理解后面所有查询的基础,因为它是用一套数学符号来描述“怎么从表里取数据”的。关系代数一共有八种运算:并、差、交、笛卡尔积、投影、选择、连接、除。但真正的基本运算只有五种:并、差、笛卡尔积、投影、选择。其余三种都能由这五种推出。
这就像乐高的五种基础积木:有了并(合并两表行)、差(减去某表行)、笛卡尔积(两表所有行两两组合)、投影(只挑某些列)、选择(只挑满足条件的行),你就能搭出任何查询。比如经典的 SPJ 数据库问题——“求供应工程 J1 零件的供应商号码”,写成关系代数就是:
π Sno (σ Jno='J1' (SPJ))π 是投影,表示只取供应商号这一列;σ 是选择,表示筛选出工程号为 J1 的行。先选行再投影列,两步就完成了查询。再复杂一点——“求没有使用天津供应商生产的红色零件的工程号”,就需要先把供应商表 S、零件表 P、供应表 SPJ 做连接,再用差运算把“用了红色天津零件的工程号”从全部工程号里减掉。整个过程像是拼积木,每一步的输入输出都是一张新表。
2.3 等值连接与自然连接:一字之差,结果截然不同
连接运算是最容易翻车的地方,尤其是等值连接和自然连接。很多人以为两者是一回事,其实差别很关键。
等值连接就是拿两张表中属性值相等的行拼在一起,连接条件是“=”。比如拿 S 表和 SPJ 表,条件设为 S.SNO = SPJ.SNO,结果会生成一张包含两张表全部列的新表,哪怕两列重名也照留不误。
自然连接则是等值连接的特例,它要求两个表中参与比较的属性必须是同名的属性组,而且在结果中会自动把那个重复的同名列去掉。比如 S 表和 SPJ 表都有 SNO 列,自然连接后,SNO 只保留一列,而不是两列。
区别可以这样记:等值连接是“条件相等就拼起来,重复列我不管”,自然连接是“相等且同名才拼,拼完帮我删掉重复的那列”。实际做查询时,如果你用的 SQL 里写的是 JOIN ... ON 两列相等,那更像等值连接;如果直接写 NATURAL JOIN,那就是自然连接。这个区分在写作业和考试时几乎是必考的点。
提示:连接还有更细的分类,比如内连接、外连接、左连接、右连接,本质都是在“等值/自然”基础上决定哪些不匹配的行要不要保留。
3. SQL 语言:你只管说“要什么”,不用管“怎么找”
SQL 是数据库系统概论里比重最大的部分,也是你毕业后真正会天天用的东西。但初学时最容易懵的不是语法,而是它和普通编程语言的思维差异。普通编程语言你写的是“怎么做”——先循环、再判断、然后拼接;SQL 你写的是“要什么”——给我满足这些条件的记录。这个差异,就是 SQL 所谓的“高度非过程化”。
3.1 非过程化:点菜式操作,后厨的事交给系统
SQL 的五个特点里,最核心的就是“高度非过程化”。用 SQL 操作数据,你只需要提出“做什么”,不需要指明“怎么做”。系统会自动决定怎么查最快、走哪个索引、用什么顺序访问表。
打个比方,你去餐厅点菜,只需要说“来一份宫保鸡丁”,至于后厨是先切肉还是先备料、用大火还是小火,你完全不用管。SQL 就是那个点菜动作,数据库管理系统就是后厨。而传统编程语言像是你钻进后厨,每一步都得自己操刀。
这个设计带来了一个直接好处:同样的查询,换了不同的数据库产品,SQL 语句几乎不用改,因为“怎么做”是由各家的系统各自优化的。这也是 SQL 能成为标准语言的原因——它把“用户意图”和“机器实现”彻底分开了。
SQL 的另外几个特点也不是摆设。综合统一,指它集数据定义(DDL)、数据操纵(DML)、数据控制(DCL)于一体,建表、查询、授权都能用一套语法完成。面向集合,指它的操作对象和结果都是元组的集合,一次能插入或删除多行。两种使用方式,指 SQL 既能作为独立语言在终端交互使用,也能嵌入到高级语言里配合程序使用。最后“语言简捷,易学易用”,你不是已经感受到了吗?
3.2 建表与查询:一段 SQL 读懂核心语法
SQL 语法的骨架很简单,就四类:CREATE 建表、SELECT 查询、UPDATE 更新、DELETE 删除。我们拿答案册里的 SPJ 数据库举例。先建供应商表:
CREATE TABLE S ( Sno CHAR(2) UNIQUE, Sname CHAR(6), Status CHAR(2), City CHAR(4) );这个语句的意思很清楚:建一张叫 S 的表,有四列,Sno 是字符型且不能重复(UNIQUE),其他列也各自限定了长度。建表时需要想清楚两件事:每列的数据类型和取值范围,以及哪些列有唯一性要求。
建完表就能查了。比如“找出所有供应商的姓名和所在城市”,就两行:
SELECT Sname, City FROM S;再比如“求供应工程 J1 零件 P1 的供应商号码”,要的是同时满足两个条件的记录:
SELECT DISTINCT SNO FROM SPJ WHERE JNO='J1' AND PNO='P1';这里的 DISTINCT 是去重,因为同一个供应商可能给 J1 供过好几次不同批次的零件,只要号码出现一次就够了。WHERE 后面跟的是筛选条件,AND 表示必须同时成立。写的时候注意字段名别写错,字符串用单引号,这就是初学者最容易踩的坑。
3.3 视图:一扇不会存数据的窗户
视图是 SQL 里特别容易理解错的概念。很多人以为视图就是一张“临时表”,其实它的本质更像是一扇窗户——你透过窗户能看到院子里的花,但窗户本身不种花。
定义上说,视图是从一个或几个基本表导出的表,本身不独立存储在数据库中,数据库里只存放视图的定义,不存放视图对应的数据。这些数据仍然存放在原来的基本表里。所以视图也叫虚表。用户在概念上可以像用基本表一样使用视图,甚至可以在视图上再定义视图。
视图有什么好处?答案册总结了四点:简化用户操作,让用户从多角度看待同一数据,对重构数据库提供逻辑独立性,以及对机密数据提供安全保护。最后这点特别有用——你不想让某个用户看到工资列,就可以建一个不含工资列的视图,把视图的查询权限给他,而不是直接给整张表的权限。
但视图也有个硬伤:不是所有视图都能更新。基本表的行列子集视图一般可以更新,但如果视图的属性来自集合函数(比如 MAX、AVG)、表达式,那它肯定不能更新。道理也简单,视图本身不存数据,对视图的更新必须转换成对基本表的更新。如果这种转换不能“唯一有意义”地完成,系统就只能拒绝更新。比如一个视图展示“每个部门的最高工资”,你没法把这个视图里的某一行的“最高工资”改掉而不引起歧义——改哪个员工的工资?系统不知道,所以不许改。
4. 安全性与完整性:数据防贼和防错的易混区分
数据库系统概论里有两个概念经常被搞混:安全性和完整性。它们听起来都在“保护数据”,但防的东西完全不同。安全性防的是“人”——防止非法用户偷偷访问、篡改数据;完整性防的是“数据本身”——防止不符合语义的坏数据混进来。打个比方,安全性是给数据装门锁,不让坏人进来;完整性是给数据配质检员,就算人进来了,录入的数据不对也得拦下来。
4.1 安全性控制:五道防线层层设卡
数据库的安全性控制常见方法有五种:用户标识和鉴别、存取控制、视图机制、审计、数据加密。这五道防线是层层递进的。
用户标识和鉴别是第一道门——你告诉系统你是谁,系统核对用户名和口令,通过才放行。这道门只解决“你是谁”的问题,不解决“你能干什么”。
存取控制是第二道门——你进来了,但系统要检查你有没有权限读这张表、改这条记录。比如你只是普通员工,系统就不会让你执行 DELETE 语句。
视图机制是第三道门——就算你有查询权限,也只能查到被允许看到的那部分数据。为了不让某个用户看见别人的工资,给他一个不含工资列的视图就行。
审计是第四道门——它不拦人,而是把一切操作记录下来,记下“谁在什么时间对哪条数据做了什么”。如果数据出了事,DBA 能靠审计日志回放现场,找出肇事者。
数据加密是最后一道防线——就算有人把硬盘偷走了,拿到的是加密后的乱码,没有密钥也还原不出内容。
4.2 自主存取控制 DAC 与强制存取控制 MAC 的区别
这两者是最容易混淆的一对。答案册里的解释很长,用一句话总结就是:自主存取控制是“我自己说了算”,强制存取控制是“系统说了算”。
自主存取控制(DAC)的逻辑是:数据的所有者有权决定谁能访问自己的数据。比如你是表的创建者,你可以用 GRANT 语句把查询权限授给同事,还能用 WITH GRANT OPTION 让他也获得转授权的权力;不想给了,就用 REVOKE 收回。它灵活,但有个弱点——权限可以在用户间传递,容易失控。
强制存取控制(MAC)完全不同。每个数据对象都被系统强制标上一个密级(绝密、机密、可信、公开),每个用户也被强制授予一个许可证级别。用户只能访问级别不高于自己许可证的数据。这个过程不依赖任何人的主观意愿,系统说了算。
两者的对比可以看这张表:
| 对比维度 | 自主存取控制 DAC | 强制存取控制 MAC |
|---|---|---|
| 决策者 | 数据所有者 | 系统强制规则 |
| 核心语句 | GRANT / REVOKE | 密级标签 + 许可证 |
| 灵活性 | 高,权限可转授 | 低,规则硬性 |
| 安全级别 | C2 级 | B1 级 |
| 防违规传播 | 较弱 | 强 |
实际应用中,商用数据库一般用 DAC 为主,涉密系统才需要 MAC。GRANT 语句长什么样?看这个例子:
GRANT SELECT, INSERT ON Student TO 王平 WITH GRANT OPTION;意思是把 Student 表的查询和插入权限授给用户王平,并且允许他再转授给别人。收回权限就写:
REVOKE INSERT ON Student FROM 王平 CASCADE;CASCADE 表示级联收回——如果王平把权限转授给了别人,那些人的权限也一并收回。
4.3 完整性约束的六种分类与违约处理
数据库的完整性靠约束条件来保证,这些约束条件可以分成六类:静态列级、静态元组、静态关系、动态列级、动态元组、动态关系。初学者常在这里迷失,其实背后的分类逻辑特别简单:先看“静态还是动态”,再看“约束对象是谁”。
静态是针对结构不变的场景,动态是针对修改变动的场景。列级约束管单个列的值,元组约束管一行内各列之间的关系,关系约束管多行或多表之间的关系。举个例子,“年龄不能超过 60 岁”是静态列级约束;“工资不低于部门平均工资的 60%”是元组级或关系级约束;“更新工资后旧值和新值之间的差距不能超过 20%”就是动态列级约束。
违反约束时系统的处理方式也值得注意。答案册明确说:违反实体完整性和用户定义完整性,系统一般直接拒绝执行;但违反参照完整性不一定会被拒绝,而是要根据应用语义做附加操作。比如删除一个供应商,如果他在 SPJ 表里有供应记录,系统可能采取三种做法之一:级联删除(把他的供应记录一起删掉)、受限删除(不允许删)、置空值删除(把他相关记录里的供应商号置空)。到底用哪种,取决于业务上“失去供应商后,供应记录还有没有保留价值”。
5. 函数依赖与范式:从 1NF 到 BCNF,表该怎么拆才算好
数据库设计中最让人摸不着头脑的就是范式了。1NF、2NF、3NF、BCNF,背定义时觉得每个字都认识,做题时却不知道拿哪些属性去“套”。要理解范式,关键要先把函数依赖搞明白。函数依赖不复杂,它就是描述“一个值能不能唯一确定另一个值”。
5.1 函数依赖:一张身份证定终身
定义里说,如果两个元组在 X 上的值相等,在 Y 上的值也一定相等,就称 Y 函数依赖于 X,记作 X→Y。通俗地说,X 能唯一确定 Y。比如学号能确定姓名,因为同一个学号只会对应一个人,所以 Sno→Sname。但姓名不能确定学号,因为可能重名。
函数依赖有三种特殊形态特别容易考:完全函数依赖、部分函数依赖、传递函数依赖。
完全函数依赖是指 X→Y,但 X 的任何真子集都不能确定 Y。比如 (Sno, Pno)→Qty,只有同时知道学号和课程号才能确定成绩,单独任何一个都确定不了。
部分函数依赖是指 X→Y,但 X 的某个真子集也能确定 Y。比如 (Sno, Class)→Dept,其实光靠 Sno 就能确定 Dept,那么 Y 对 X 就是部分函数依赖。
传递函数依赖是指 X→Y,Y→Z,且 Y 不能确定 X。比如 Sno→Class,Class→Dept,那么 Sno→Dept 就是传递依赖。
在答案册的“学生”关系模式里,Sno→Class→Dept→Rno(宿舍区),所以学号和宿舍区之间存在传递依赖。这会造成什么问题?如果某个系换了宿舍区,你就得把所有该系学生的宿舍区逐一更新,漏改一条数据就出错了。这就是冗余带来的更新异常。
5.2 范式递进:每爬一层,消除一类“毛病”
范式的本质就是对表的拆分程度做的等级划分。每升一级,就消灭一类隐患。
第一范式(1NF)要求表里的每个分量都是不可再分的原子值。什么意思?就是不能在一格里存“北京,海淀”这种带逗号的两个信息。这是底线,几乎所有关系表都天然满足。
第二范式(2NF)要求在 1NF 基础上消除非主属性对码的部分函数依赖。换句话说,如果你的码是复合的(比如学号+课程号),那所有非主属性都得完整地依赖整个码,不能只依赖其中一半。比如一张表里码是 (学号, 课程号),但“姓名”只依赖学号,它跟课程号没关系,这就是部分依赖,这张表就不满足 2NF。解决办法是把表拆开:学生表和选课表分开存。
第三范式(3NF)要求在 2NF 基础上消除传递依赖。也就是说,非主属性不能“绕路”依赖主码。比如学生表里学号确定系名,系名确定宿舍区,那么宿舍区就和学号之间隔着系名,这就是传递依赖。拆法是把“系”单独拎出来成一张表,学生表里只留系编号。
BCNF 是 3NF 的加强版。3NF 允许“码”和“非主属性”之间存在一些不那么完美的依赖关系,BCNF 干脆规定:任何决定因素都必须包含码。也就是说,只要某个属性能决定其他属性,它自身就得是码的一部分。这是判断一张表是否“纯洁”的高标准。
5.3 范式是拆表的尺子,不是越拆越好
那是不是范式等级越高越好?不一定。答案册里有一个经典的结论:任何一个二目关系(只有两个属性的关系)都一定是 3NF,也一定是 BCNF。因为只有两个属性时,一个属性要么单独是码,要么两个一起是码,不存在传递依赖,也不存在非码属性决定另一个属性的情况。所以做题时遇到只有两个属性的关系,可以直接判 BCNF。
但现实中不建议把表无条件拆到最高范式。范式解决的是“更新时不出错”,但代价是查询时要多连接好几张表,性能会下降。一个常用的做法是:频繁更新的事务型场景,尽量做到 3NF 甚至 BCNF;报表类、分析类的只读场景,反而可以刻意保留一些冗余,把表“退”到 2NF,用空间换查询速度。拆表没有绝对标准,只有针对业务场景的选择。
6. 数据库设计六阶段:从 E-R 图到建表,拆到什么程度才算够
数据库系统概论的最后一章把整个设计过程总结成六个阶段:需求分析、概念结构设计、逻辑结构设计、数据库物理设计、数据库实施、数据库运行和维护。这六个阶段是一个完整项目的全生命周期。
需求分析搞清楚用户到底要什么数据、要做什么处理。概念结构设计把需求抽象成一个独立于具体数据库产品的概念模型——最常见的就是 E-R 图,用实体、属性和联系描述现实世界。逻辑结构设计把 E-R 图转换成某个具体数据库支持的关系模型,也就是实际建表。物理设计考虑存储结构和索引。实施就是把设计变成真实运行的数据库,调试程序,导入数据。运行维护则是在长期使用中不断评估和调整。
概念结构设计阶段有一个策略选择问题:自顶向下(先定全局框架再细化)、自底向上(先做局部再集成)、逐步扩张(从核心往外扩)、混合策略(两者结合)。做小项目时用自底向上比较顺手,做大型系统时通常得混合着来。
范式在这里的作用是“逻辑结构设计阶段的优化工具”——表结构初步确定后,用范式去检查每个关系模式,如果有部分依赖或传递依赖,就考虑拆表。但拆到什么程度,要看查询需求。
我记得有次给某个跨平台系统的数据模块设计表结构,一上来把所有表都拆到 BCNF,结果报表查询要 join 七八张表,慢到客户端直接超时。后来才发现,报表场景其实更适合保留冗余字段,事务表才需要严格范式化。从那以后我每次设计表都会先问自己一个问题:这批数据是“经常被更新”还是“经常被读取”?前者的表老老实实上 3NF 以上,后者的表大胆保留冗余。这门课的内容一环扣一环——关系模型给你表和运算的框架,SQL 让你高效取数,完整性守质量底线,范式帮你把表拆得清晰、设计出不易出错的结构。希望这篇讲透核心概念的梳理能帮到你,再去翻那本答案册时,每一章的标题应该都不再陌生了。
本文还有配套的精品资源,点击获取