简介:本资源是《数据库系统概论(第5版)》王珊、萨师煊主编教材的配套课后习题参考答案,面向高校计算机、软件工程、信息管理等专业本科生及数据库初学者,用于辅助理解关系模型、SQL语言、关系代数等核心理论与实践难点。文件为单个Word文档(.doc),大小588KB,内容覆盖第2章关系数据库与第3章SQL语言全部重点习题,含关系模型三要素、完整性约束判定、等值连接与自然连接辨析、SPJ关系代数表达式推导、SQL建表与多表查询语句编写等典型题型的详细解答与解题思路。答案表述规范、步骤清晰,部分题目还附有关键语法说明与易错点提示,便于自学核对与考前复习。目前已有1723人学习下载,是夯实数据库理论基础、提升SQL实战能力的实用参考资料。
1. 这不是“背答案”的资料,而是一份帮你把数据库从“黑匣子”变成“透明积木”的入门地图
你刚翻开《数据库系统概论(第5版)》,看到“关系代数”“参照完整性”“BCNF”这些词,是不是像站在一扇厚重的金属门前——门上没把手、没锁孔、连缝都看不见?翻两页就卡在“πSno(σJno=‘J1’(SPJ))”这串符号里,心里发问:这到底是在算数学题,还是在解密码?别急,这份标着“王珊版答案”的.doc文件,根本不是让你抄作业的参考书,而是一张被前辈亲手画满批注、踩过坑、标好路标的实操地图。它用最朴素的问答体,把抽象概念全锚定在“SPJ”这个四张表的小世界里:谁供应零件、哪个工程用了什么颜色的货、为什么外码能为空、怎么用一条SELECT找出“没用天津红零件的工程号”……所有理论,都长在具体数据、具体操作、具体错误上。它适合你——零基础但想真正搞懂“数据库到底在干啥”的人,不是为考试突击刷题,而是为以后写代码时不再对着FOREIGN KEY发呆、建表时不再乱加NULL、查数据时不再怀疑自己写的 SQL 是不是漏了括号。它不讲高深算法,只解决一个最原始的问题:当你说“我要存点数据”,系统到底在背后做了哪些事,又凭什么敢保证“不会丢、不会错、不会让不该看的人看见”?这份答案,就是带你一层层掀开那扇金属门,看清里面齿轮怎么咬合的第一份说明书。
2. 先搞懂一个事实:数据库不是“电子表格”,而是一套有脾气、讲规矩的协作系统
很多人学数据库,第一反应是“不就是 Excel 多几行?”——这是最典型的认知偏差,也是后续所有翻车的起点。这份答案文档,从第二章第一个问题就开始掰开揉碎讲清这件事:关系模型的三个组成部分,就是数据库的“骨架、神经和血液”。它不是凭空造出来的,而是为了解决真实协作中的混乱。我们用一个生活场景来类比:假设你和三个同学一起做毕业设计,要管项目进度、成员分工、提交的文档。如果用 Excel,可能一人一个 sheet,改完发群里,最后合并时发现 A 同学删了 B 同学填的截止日期,C 同学把“已完成”写成“已成”,没人知道哪版是准的。而数据库,就像给你们配了一个带自动校验的共享白板:
- 关系数据结构(骨架):它强制规定“进度表”必须有“任务名、负责人、状态、截止日”这四个格子,且“负责人”这一格只能填你们四个人的名字(不能填“隔壁班老王”),这就是“实体完整性”;
- 关系操作集合(神经):你想查“所有状态为‘进行中’的任务”,不用一行行翻,只说
SELECT * FROM 进度表 WHERE 状态 = '进行中',系统自动扫全表、比对、返回结果——它不关心你怎么找,只负责“精准送达”,这就是“非过程化”; - 关系完整性约束(血液):当有人想把“负责人”填成空,系统立刻拦住:“不行!每个任务必须有负责人!”;或者有人想在“分工表”里填一个根本不存在的“任务名”,系统也拒绝:“这个任务名在‘进度表’里找不到,不能认!”——这些硬性规则,就是数据库的“免疫力”,防止垃圾数据污染整个系统。
这份答案文档里反复出现的 SPJ 案例(S供应商、P零件、J工程、SPJ供应关系),就是这个协作系统的最小完整切片。你看它怎么用π(投影)和σ(选择)组合出“供应J1工程的供应商号”,就是在看系统如何用最基础的“取列”和“筛行”两个动作,搭出千变万化的查询逻辑。它不教你炫技,只告诉你:所有复杂功能,都源于这五个基本运算(并、差、笛卡尔积、投影、选择)的排列组合。就像乐高,再酷的机器人,也由方块、圆柱、斜面这些基础件拼成。理解这一点,你就不会被后面“除运算”“自然连接”吓退——它们只是基础件的新拼法,不是新零件。
2.1 关系代数不是数学考试,而是描述“我要什么数据”的通用手语
很多新手看到关系代数符号就头皮发麻,觉得是数学题。其实完全相反:它是比 SQL 更接近人话的“需求翻译器”。比如问题6的第一小问:“求供应工程 J1 零件的供应商号码 SNO”。你脑子里想的是什么?是不是“去 SPJ 表里,把所有 JNO 是 ‘J1’ 的那些行找出来,然后只留下 SNO 这一列”?关系代数就是把这句话直译成符号:πSno(σJno=‘J1’(SPJ))。
σJno=‘J1’(SPJ):σ(sigma)是“筛选”动作,意思是“从 SPJ 表里,挑出 JNO 列值等于 ‘J1’ 的所有行”;πSno(...):π(pi)是“取列”动作,意思是“把上面筛选出来的那些行,只保留 SNO 这一列”。
它不关心数据库内部怎么实现,只清晰表达你的意图。这种“先想清楚我要什么,再告诉系统”的思维,正是数据库思维的核心。SQL 里的SELECT Sno FROM SPJ WHERE Jno='J1',就是这句手语的“普通话版本”,本质完全一样。文档里特意把关系代数和 SQL 并列给出(比如第3章习题4),就是在告诉你:别被符号吓住,它们都是同一种逻辑的不同表达。你练熟了σ和π,SQL 的WHERE和SELECT就自然通透。
2.2 “完整性约束”不是条文,而是数据库给你配的“防错安全带”
新手常问:“为什么主键不能为 NULL?”“为什么外码有时能空,有时不能?”这些问题的答案,就藏在“完整性约束”这四个字里。它不是冷冰冰的教条,而是数据库为你预设的“安全带”,防止你无意识犯错。文档第2章第5题讲得特别透:
- 实体完整性:比如“供应商表 S”的主键是
SNO,那每一行的SNO就像人的身份证号,必须有、且唯一。如果允许为空,系统就分不清“这个供应商还没填号”和“这个供应商根本不存在”——数据就乱了。所以规则是:“主属性不能取空值”。 - 参照完整性:这是新手最容易懵的点。文档里说“外码可以为空,但前提是它本身不是主属性”。我们用 SPJ 表解释:
SPJ(Sno, Pno, Jno, Qty),其中Sno是外码(参照 S 表),Pno是外码(参照 P 表),Jno是外码(参照 J 表)。如果某条记录的Jno是空的,意味着“这个供应关系还没确定给哪个工程”,这在业务上是合理的(比如零件先备好,工程还没立项)。但Sno如果为空,就等于“不知道是谁供应的”,这在供应链里是不可接受的。所以规则是:外码能为空,仅当它在本表中不承担“唯一标识”作用时。这个“为什么”,文档用“属性 F 本身不是主属性,则可以取空值”一句话点破,比死记硬背管用十倍。
2.3 SQL 不是命令行,而是你和数据库之间的“自然对话协议”
很多人学 SQL,上来就背CREATE TABLE语法,结果写到JOIN就晕。这份答案文档的聪明之处,在于它把 SQL 当作“对话”来教。第3章第1题总结的五大特点,每一条都在解决一个沟通痛点:
- 综合统一:你不用切换三种语言——建表(DDL)、查数据(DML)、管权限(DCL),全用 SQL;
- 高度非过程化:你只说“我要所有上海供应商的零件”,不用告诉系统“先查 S 表找上海的,再查 SPJ 表匹配,再关联 P 表取名字”,它自动规划最优路径;
- 面向集合:
UPDATE P SET COLOR='蓝' WHERE COLOR='红'这一句,不是改一个零件,而是把所有红色零件“批量染蓝”,思维从“单个对象”升维到“整组数据”。
文档里所有 SQL 示例,都紧贴 SPJ 场景。比如第3章习题4的第(3)问:“求供应工程 J1 零件为红色的供应商号码”,SQL 写成:
SELECT SNO FROM SPJ,P WHERE JNO='J1' AND SPJ.PNO=P.PNO AND COLOR='红'这里没有用JOIN关键字,而是用经典的“逗号连接 + WHERE 条件”写法。为什么?因为这是最直白的“对话”:
- “从 SPJ 表和 P 表里找数据”(
FROM SPJ,P); - “条件是:SPJ 的 JNO 必须是 ‘J1’”(
JNO='J1'); - “SPJ 的零件号必须等于 P 表的零件号”(
SPJ.PNO=P.PNO); - “而且 P 表的颜色必须是 ‘红’”(
COLOR='红')。
它像在跟朋友描述:“你去两个名单里对照着找,满足这三个条件的人,把他们的编号给我。” 理解了这种“说人话”的逻辑,INNER JOIN、LEFT JOIN就不再是魔法咒语,而是更精确的“对照方式”。
3. 第一步别碰代码,先动手画一张属于你的“SPJ 四表关系图”
所有数据库学习的崩溃点,往往始于“脑内没地图”。你对着SELECT ... FROM SPJ,P WHERE ...发呆,不是因为不懂语法,而是根本没在脑子里构建出 S、P、J、SPJ 这四张表长什么样、它们之间怎么连。这份答案文档最大的价值,就是它所有的理论、所有习题,都牢牢钉在 SPJ 这个极简案例上。所以,你入门的第一步,不是打开电脑,而是拿出一张纸,亲手画出这四张表的结构和关系。这不是形式主义,而是建立空间感的必经之路。我们按文档第3章习题4给出的建表语句来画:
3.1 用三分钟,画出四张表的“身份证”和“连接线”
先看每张表的“身份证”(即字段和主/外键):
- S 表(供应商):
Sno(主键),Sname,Status,City—— 文档里建表语句CREATE TABLE S (Sno C(2) UNIQUE...)明确Sno是唯一标识; - P 表(零件):
Pno(主键),Pname,Color,Weight——Pno是零件身份证; - J 表(工程):
Jno(主键),Jname,City——Jno是工程身份证; - SPJ 表(供应关系):
Sno,Pno,Jno,Qty—— 注意!这里Sno、Pno、Jno都不是主键,但文档明确指出它们是外码,分别指向 S、P、J 表的主键。
现在画“连接线”:
- SPJ 的
Sno→ 指向 S 表的Sno(一根线); - SPJ 的
Pno→ 指向 P 表的Pno(第二根线); - SPJ 的
Jno→ 指向 J 表的Jno(第三根线)。
这张图,就是你的数据库“神经系统”。以后任何查询,比如“找上海供应商的红色零件”,你第一反应就该是:
- 上海供应商 → 查 S 表,
City='上海'; - 红色零件 → 查 P 表,
Color='红'; - 它们怎么关联?→ 必须经过 SPJ 表,因为只有 SPJ 记录了“谁(S)供应了什么(P)给哪个工程(J)”。
所以 SQL 必然是FROM S, P, SPJ WHERE S.Sno=SPJ.Sno AND P.Pno=SPJ.Pno AND S.City='上海' AND P.Color='红'。画图的过程,就是把抽象关系具象化的过程。很多高手写 SQL 前必画 ER 图,原因就在此——脑子有图,手才不抖。
3.2 用一个真实查询,验证你的“关系图”是否画对
我们来实战验证。文档第3章习题4的第(4)问:“求没有使用天津供应商生产的红色零件的工程号 JNO”。这个问题有点绕,我们拆解:
- “天津供应商” → S 表,
City='天津'; - “红色零件” → P 表,
Color='红'; - “天津供应商生产的红色零件” → 需要 S、P、SPJ 三表连接:
S.Sno=SPJ.Sno AND P.Pno=SPJ.Pno AND S.City='天津' AND P.Color='红'; - “使用了这些零件的工程号” → 上面连接的结果里,
SPJ.Jno就是这些工程号; - “没有使用”的工程号 → 就是所有工程号(
SELECT Jno FROM J)减去上面得到的工程号。
文档给出的 SQL 是:
SELECT DISTINCT JNO FROM SPJ WHERE JNO NOT IN ( SELECT JNO FROM SPJ,P,S WHERE S.CITY='天津' AND P.COLOR='红' AND S.SNO=SPJ.SNO AND P.PNO=SPJ.PNO )现在,对照你的关系图:
- 子查询里
SPJ,P,S三表连接,线是否都画对了?(S→SPJ via Sno, P→SPJ via Pno) - 主查询
SELECT JNO FROM SPJ,为什么不是FROM J?因为 SPJ 表里只存在“被使用过”的工程号,J表里可能有未开工的工程,但题目要求的是“没有使用”,所以必须以 SPJ 为基准——这恰恰说明你的图里,SPJ 是核心枢纽。
如果你的图能清晰支撑这个逻辑,恭喜,你已经拿到了数据库世界的“导航仪”。
3.3 避坑:新手画关系图最容易踩的3个坑
提示:画图不是为了好看,是为了让逻辑在脑子里“立起来”。以下坑,我第一次学时全踩过,血泪经验。
坑1:把外键当成主键,导致关系错位
- 现象:在 SPJ 表里,把
Sno或Pno也标成主键(加粗或打星),画连接线时犹豫不决。 - 原因:混淆了“唯一标识”和“关联依据”。SPJ 的主键是
(Sno, Pno, Jno)这个组合(文档虽未明说,但业务逻辑决定:同一供应商对同一工程供应同一零件,数量唯一),单个Sno只是关联 S 表的“钥匙”,不是 SPJ 自己的“身份证”。 - 解决:画图时,只给 SPJ 表标一个组合主键框,外键用虚线箭头单独指向 S/P/J 表,标注“外码,参照 S.Sno”等。
坑2:忽略“空值”的业务含义,画出不可能的关系
- 现象:画图时,给 SPJ 的
Jno字段加了“NOT NULL”约束,认为所有供应必须对应工程。 - 原因:脱离业务。文档第2章第5题明确说“外码可为空”,比如零件已入库(SPJ 记录存在),但采购合同还没签(Jno 未知),此时
Jno为空完全合理。强行加 NOT NULL,会扼杀真实业务流。 - 解决:在图上给
Jno字段旁手写小字:“可为空,表示工程未确定”。
坑3:连接时搞混“谁连谁”,写出永远为空的查询
- 现象:写
SELECT * FROM S, P WHERE S.Sno = P.Pno,运行结果为空,百思不得其解。 - 原因:Sno 和 Pno 是不同维度的编码(供应商号 vs 零件号),直接比较毫无意义。连接必须通过“中间人”SPJ,因为只有 SPJ 同时拥有这两个字段,并定义了它们的关系。
- 解决:画图时,S、P、J 三张表绝不直接连线!所有连接线必须经过 SPJ 这个“十字路口”。这是铁律。
4. 从“抄答案”到“真跑通”:用 SQLite 亲手执行第一个 SPJ 查询
光看答案、光画图,不亲手敲一遍,知识就浮在半空。现在,我们把文档里的理论,变成你电脑上真实跳动的数据。别怕没装数据库——SQLite 是一个文件就是一个数据库的轻量级引擎,零配置,5分钟搞定。我们用它来创建 SPJ 四张表,并执行文档第3章习题4的第一个查询:“求供应工程 J1 零件的供应商号码 SNO”。
4.1 三步安装 SQLite,比装微信还简单
- 下载:访问官网 sqlite.org/download.html(搜索“sqlite-tools”),下载对应你电脑系统的预编译二进制包(Windows 下是
sqlite-tools-win32-*.zip,Mac 下是sqlite-tools-osx-*.zip); - 解压:把 zip 包解压到任意文件夹,比如
D:\sqlite; - 启动:进入解压目录,双击
sqlite3.exe(Windows)或在终端输入./sqlite3(Mac/Linux),你会看到一个sqlite>提示符——恭喜,你的数据库已就绪!
注意:SQLite 不需要“服务端”,没有用户名密码,一个
.db文件就是完整的数据库。这对新手极其友好,没有环境配置的玄学障碍。
4.2 用文档里的建表语句,亲手创建四张表
我们严格按文档第3章习题4的CREATE TABLE语句来(已根据现代 SQL 标准微调类型):
-- 创建 S 表(供应商) CREATE TABLE S ( Sno TEXT PRIMARY KEY, Sname TEXT, Status INTEGER, City TEXT ); -- 创建 P 表(零件) CREATE TABLE P ( Pno TEXT PRIMARY KEY, Pname TEXT, Color TEXT, Weight INTEGER ); -- 创建 J 表(工程) CREATE TABLE J ( Jno TEXT PRIMARY KEY, Jname TEXT, City TEXT ); -- 创建 SPJ 表(供应关系) CREATE TABLE SPJ ( Sno TEXT, Pno TEXT, Jno TEXT, Qty INTEGER, -- 添加外键约束(SQLite 支持,但需开启) FOREIGN KEY (Sno) REFERENCES S(Sno), FOREIGN KEY (Pno) REFERENCES P(Pno), FOREIGN KEY (Jno) REFERENCES J(Jno) );把以上 SQL 复制粘贴到sqlite>提示符下,每行后回车。如果看到Query executed successfully或无报错,说明表创建成功。你可以用.tables命令查看当前有哪些表。
4.3 插入测试数据,让 SPJ 活起来
文档里没给数据,但我们可以用习题6的查询反推。比如,要查“供应 J1 的供应商”,至少得有一条Jno='J1'的记录。我们插入几条典型数据:
-- 插入供应商 INSERT INTO S VALUES ('S1', '精益公司', 20, '天津'); INSERT INTO S VALUES ('S2', '宏达厂', 10, '上海'); -- 插入零件 INSERT INTO P VALUES ('P1', '螺栓', '红', 10); INSERT INTO P VALUES ('P2', '轴承', '蓝', 20); -- 插入工程 INSERT INTO J VALUES ('J1', '一号厂房', '北京'); INSERT INTO J VALUES ('J2', '二号车间', '上海'); -- 插入供应关系:S1 供应 P1 给 J1,S2 供应 P2 给 J2 INSERT INTO SPJ VALUES ('S1', 'P1', 'J1', 100); INSERT INTO SPJ VALUES ('S2', 'P2', 'J2', 200);每条INSERT执行后,SQLite 会返回1 row inserted。现在,你的 SPJ 数据库就活了!
4.4 执行第一个查询,亲眼见证“πσ”如何变成屏幕上的结果
回到文档第3章习题4的第(1)问:SELECT DISTINCT SNO FROM SPJ WHERE JNO='J1'。在sqlite>提示符下输入:
SELECT DISTINCT SNO FROM SPJ WHERE JNO='J1';回车!屏幕上会立刻显示:
S1就是这一行,是你亲手从零搭建的数据库里,挖出的第一颗数据钻石。它证明:
- 你理解了
WHERE是“筛选”(对应关系代数的σ); - 你理解了
SELECT DISTINCT是“取唯一列”(对应π); - 你理解了
SPJ表里JNO='J1'的行,确实关联着SNO='S1'。
这不是背出来的答案,是你指挥数据库完成的一次真实协作。接下来,你可以立刻尝试第(2)问:SELECT DISTINCT SNO FROM SPJ WHERE JNO='J1' AND PNO='P1',结果依然是S1;再试第(3)问,需要连 P 表:
SELECT SNO FROM SPJ, P WHERE SPJ.JNO='J1' AND SPJ.PNO=P.PNO AND P.Color='红';结果还是S1。每一次成功,都在加固你对“关系”二字的理解。
5. 避坑:新手执行 SQL 时最常遇到的5个“当场宕机”问题
提示:这些问题,90% 的新手在第一次敲 SQL 时都会撞上。别慌,它们不是你的错,是 SQLite 在用它的方式和你打招呼。
问题1:输入 SQL 后按回车,光标不动,像卡住了
- 原因:SQLite 认为你还没输完,因为 SQL 语句没以分号
;结尾。它在等你补全。 - 解决:立刻输入
;再回车。养成习惯:每条 SQL 结尾必须加;。
问题2:报错no such table: SPJ
- 原因:要么表名拼错了(比如写成
spj小写,SQLite 默认大小写敏感),要么前面建表语句执行失败(比如复制时多了空格),要么你打开了新的sqlite3窗口,之前的表没加载。 - 解决:输入
.tables查看当前有哪些表;确认建表语句无误;确保所有操作在同一个sqlite3会话中进行。
问题3:SELECT * FROM SPJ返回空,但明明插入了数据
- 原因:
INSERT语句没加;结尾,SQLite 没执行,只是把它当成了多行输入。 - 解决:检查
INSERT后是否有;;执行.schema SPJ看表结构是否正确;重新执行带分号的INSERT。
问题4:WHERE JNO='J1'查不到,但WHERE JNO='j1'能查到
- 原因:SQLite 默认区分大小写,
'J1'和'j1'是两个不同字符串。文档里所有例子都是大写,你插入时也必须大写。 - 解决:统一用大写;或在
WHERE中用UPPER(JNO)='J1'强制转大写(但性能略低)。
问题5:外键约束不生效,INSERT INTO SPJ VALUES ('S999', 'P1', 'J1', 100)竟然成功了
- 原因:SQLite 默认不启用外键约束(为兼容旧版本)。这是最大陷阱!
- 解决:在创建任何表之前,先执行
PRAGMA foreign_keys = ON;。这是必须的第一步!完整流程:
启用后,再插sqlite> PRAGMA foreign_keys = ON; sqlite> CREATE TABLE S (...); sqlite> CREATE TABLE SPJ (...);S999就会报错FOREIGN KEY constraint failed,这才是你想要的“安全带”。
6. 下一步学什么:从“会查”到“会设计”,一条不绕弯的进阶路径
当你能熟练地在 SQLite 里创建 SPJ 四表、插入数据、执行SELECT/INSERT/UPDATE/DELETE,并理解每条语句背后的“关系”逻辑时,恭喜你,已经跨过了数据库学习最陡峭的坡。下一步,不是去啃《数据库系统实现》这种大部头,而是沿着一条清晰、务实、每一步都踩在真实需求上的路径,把知识焊接到能力上。我的建议是:用两周时间,完成一个“微型图书管理系统”的闭环实践。它不大,但五脏俱全,能让你把文档里所有章节的知识点,像拼图一样严丝合缝地嵌进去。
6.1 用“需求驱动”代替“章节驱动”,亲手设计你的第一个数据库
别再从第一章开始读。打开一个空白文档,写下你的真实需求:
- “我要管理图书馆的书,每本书有书名、作者、ISBN、出版年份、分类(如‘计算机’‘文学’)”;
- “有多个管理员,每人有工号、姓名、权限等级”;
- “读者借书,要记录借阅日期、应还日期、是否已还”;
- “一本书可以被多人借,一个人可以借多本书”。
现在,对照文档的章节,把需求翻译成设计: - 第2章关系模型→ 画出“图书”“管理员”“读者”“借阅记录”四张表,标出主键(ISBN、工号、读者ID、借阅ID)和外键(借阅记录表里的“图书ISBN”和“读者ID”);
- 第3章SQL→ 为每张表写
CREATE TABLE,为“查所有未还书”写SELECT,为“添加新书”写INSERT; - 第4章安全性→ 用
GRANT SELECT ON 图书 TO 管理员控制权限(SQLite 本身权限弱,但语法要练); - 第5章完整性→ 在“借阅记录”表加
CHECK (应还日期 > 借阅日期),在“图书”表加UNIQUE(ISBN); - 第6章范式→ 当你发现“作者”字段里写了“张三,李四”,就意识到该拆成“图书-作者”关联表,这就是 1NF 到 2NF 的实战触发点。
从那以后我每次设计新表,都强制走一遍“SPJ 四表验证法”:
- 这张表的主键是什么?(能否唯一标识一行?)
- 它的外键指向谁?(有没有对应的主键表?)
- 它的字段会不会重复出现?(比如“出版社地址”在10本书里重复,就该拆成“出版社”表)
- 它的业务规则能不能用
CHECK或FOREIGN KEY写进建表语句?
这个习惯,让我在后来参与某高校选课系统开发时,避免了因“课程-教师”关系设计不当导致的排课冲突 bug。希望帮到你。
本文还有配套的精品资源,点击获取