数据库练习卷 - 逐题知识点解析
!!! info "考试重点" 本文档严格按照《练习卷2026》参考答案,逐题分析答案为什么这么写,涉及什么知识点。
**最后更新:2026-05-27** | **整理人:赫尔墨斯(小弟一号)**
一、简答题(6选4,每题5分)
题目1:实体完整性约束判断
题目: 有关系模式R(A,B,C,D),已知建表语句为:
Create table R(A char(2),B char(2),C char(2),D char(2),primary key(A,B))问当表中不存在任何数据的情况下,以下语句是否能正常运行,并回答原因:
insert into R(B,C) values('B1','C1')答案: ❌ 不能正常运行。(1分)因为主属性A的值为空值,违反实体完整性约束。(2分)
为什么这么写:
第一步:分析表结构
表R有4个列:A, B, C, D
主键是 (A, B) 联合主键第二步:分析插入语句
insert into R(B,C) values('B1','C1')- 只插入了 B 和 C 的值
- A 没有被赋值,所以 A = NULL
第三步:检查约束
主键是 (A, B)
A 是主属性
主属性不能为 NULL(实体完整性规则)
A = NULL → 违反实体完整性 → 插入失败涉及的知识点:
| 知识点 | 内容 |
|---|---|
| 实体完整性 | 主键中的所有属性(主属性)都不能取 NULL 值 |
| PRIMARY KEY 约束 | 定义主键,保证实体完整性 |
| 主属性 | 包含在候选码中的属性 |
| 插入操作 | 插入数据时,必须满足所有完整性约束 |
记忆口诀:
实体完整性 = 主键不能为空
插入数据时,主键属性必须有值题目2:主键插入判断
题目:
insert into R(A,B) values('A2','B1')答案: ✅ 可以正常运行(1分)。因为主属性A、B的值不为空值(2分)
为什么这么写:
第一步:分析插入语句
insert into R(A,B) values('A2','B1')- 提供了 A='A2',B='B1'
第二步:检查约束
主键是 (A, B)
A = 'A2'(不为空)✓
B = 'B1'(不为空)✓
表中没有任何数据,不存在重复问题 ✓
满足所有约束 → 插入成功涉及的知识点:
| 知识点 | 内容 |
|---|---|
| 实体完整性 | 主键不能为空且唯一 |
| 插入操作 | 插入数据时,主键属性必须有值且不重复 |
记忆口诀:
插入数据时,只要主键属性都有值且不重复,就能成功题目3:外键删除问题
题目: 有关系模式如下定义:
create table R(
A char(4),
B char(4),
primary key(A),
foreign key(B) references R(A)
)请解释当R关系中一个元组被删除时会发生什么情况。
答案: 该关系中存在外码,参照本关系的主码A,(3分)当删除一个元组时,如果该元组的A被其他元组的B所参照,会发生违反参照完整性约束的问题。(3分)
为什么这么写:
第一步:理解表结构
表R有两个列:A, B
主键是 A
外键是 B,引用同一张表的 A 列(自引用外键)第二步:分析删除场景
假设数据:
- 元组1:A='A1', B=NULL
- 元组2:A='A2', B='A1'(B参照了元组1的A)
如果删除元组1:
- 元组2的 B='A1' 找不到对应的 A 值了
- 违反参照完整性约束第三步:说明处理方式
可能的处理方式:
- RESTRICT/NO ACTION:拒绝删除(默认行为)
- CASCADE:级联删除(连同引用它的元组一起删除)
- SET NULL:将引用的外键设为 NULL
- SET DEFAULT:将引用的外键设为默认值涉及的知识点:
| 知识点 | 内容 |
|---|---|
| 参照完整性 | 外键的值必须要么等于被参照表中某个元组的主键值,要么为 NULL |
| 自引用外键 | 外键引用同一张表的主键(典型例子:员工-经理关系) |
| 删除约束 | 删除被参照元组时,需要考虑处理方式 |
记忆口诀:
自引用外键很常见
删除被参照元组时,需要考虑处理方式题目4:并发调度问题
题目: 请说明下图中的并发调度存在什么问题?请给出可串行化的调度。
答案: 存在不可重复读的问题。(2分) (4分)加锁,解锁正确,错一处扣1分
为什么这么写:
第一步:识别问题
不可重复读的定义:
- 同一事务内两次读取结果不同
- 原因:其他事务在两次读取之间修改了数据第二步:与脏读的区别
脏读:读取了其他事务未提交的数据
不可重复读:读取了其他事务已提交的数据,但两次读取结果不同第三步:解决方案
使用加锁机制:
- 读锁(共享锁):读取数据时加锁
- 写锁(排他锁):修改数据时加锁第四步:可串行化调度
定义:并发执行结果等价于某种串行执行
实现:使用两段锁协议(2PL)涉及的知识点:
| 知识点 | 内容 |
|---|---|
| 不可重复读 | 同一事务内两次读取结果不同 |
| 脏读 | 读取了其他事务未提交的数据 |
| 两段锁协议(2PL) | 扩展阶段只能加锁,收缩阶段只能解锁 |
| 可串行化 | 并发执行结果等价于某种串行执行 |
记忆口诀:
不可重复读 = 同一事务内两次读取结果不同
可串行化 = 并发执行结果等价于某种串行执行题目5:BCNF判断
题目: 关系模式R(U,F),U=(A,B,C,D),F={(A,B)→C,(A,C)→B,B→C},请问R是否是BCNF,并说明理由。
答案: 首先求候选码,L:A LR:BC R:- N:D ADF+ =AD ABD F+ = ABCD=U ACD F+ = ABCD=U 所以候选码为:ABD,ACD。(4分) 由于给出的FD中存在决定因素不包含码的情况,所以R不是BCNF。(2分)
为什么这么写:
第一步:求候选码
属性分类:
| 类型 | 属性 | 说明 |
|---|---|---|
| L(只出现在左边) | A | 只在函数依赖左边出现 |
| LR(左右都出现) | B, C | 在左右两边都出现 |
| R(只出现在右边) | 无 | - |
| N(都不出现) | D | 在任何一边都不出现 |
计算闭包:
ADF+ = AD(无法得到全部属性)
ABD F+ = ABCD = U ✓
ACD F+ = ABCD = U ✓
候选码为:ABD 和 ACD第二步:判断 BCNF
BCNF 要求: 对于每一个非平凡函数依赖 X→Y,X 必须是超码。
分析函数依赖:
- (A,B)→C:X=(A,B),(A,B) 不是超码(无法推出 D)
- (A,C)→B:X=(A,C),(A,C) 不是超码(无法推出 D)
- B→C:X=B,B 显然不是超码
结论: 存在决定因素不包含码的情况,所以 R 不是 BCNF。
涉及的知识点:
| 知识点 | 内容 |
|---|---|
| 候选码 | 能唯一标识元组的最小属性集 |
| 属性分类 | L类(只左)、R类(只右)、LR类(左右都出现)、N类(都不出现) |
| 闭包计算 | X⁺ = 所有能从X推出的属性集合 |
| BCNF | 每个非平凡FD的左边都必须是超码 |
记忆口诀:
BCNF判断步骤:
1. 先求候选码
2. 检查每个FD的左边是否都是超码
3. 如果存在左边不是超码的FD,则不是BCNF题目6:数据库恢复
题目: 考虑如下日志记录,假设 A、B、C 的初始值均为 0:
| 序号 | 日志 |
|---|---|
| 1 | T1:开始 |
| 2 | T1:写B,B=10 |
| 3 | T2:开始 |
| 4 | T2:写A,A=8 |
| 5 | T3:开始 |
| 6 | T3:写C,C=12 |
| 7 | T1:写C,C=11 |
| 8 | T1:提交 |
| 9 | T3:写A,A=10 |
| 10 | T2:写B,B=8 |
| 11 | T2:提交 |
如果系统故障发生在 10 之后,请说明需要重做的事务,需要撤销处理的事务。并写出系统恢复后 A、B、C 的值。
答案: 重做的事务:T1;撤销的事务:T2,T3(3分) A=0,B=10,C=11(3分)
为什么这么写:
第一步:确定事务状态
故障发生在日志10之后:
- T1:在日志8已提交 → REDO(重做)
- T2:在日志10还在执行,日志11才提交(故障发生在10之后)→ UNDO(撤销)
- T3:从未提交 → UNDO(撤销)第二步:恢复过程
A、B、C 初始值都是 0
T1 REDO(重做):
日志2:B=10
日志7:C=11T2 UNDO(撤销):
日志4:A=8(撤销,A恢复为0)
日志10:B=8(撤销,B恢复为10,因为T1已写入10)T3 UNDO(撤销):
日志6:C=12(撤销,C恢复为11,因为T1已写入11)
日志9:A=10(撤销,A保持0)第三步:最终结果
A=0,B=10,C=11涉及的知识点:
| 知识点 | 内容 |
|---|---|
| RED(重做) | 已提交的事务需要重做 |
| UNDO(撤销) | 未提交的事务需要撤销 |
| 恢复原则 | 从后往前扫描日志 |
| 事务状态 | 开始、执行、提交、故障 |
记忆口诀:
已提交重做,未提交撤销。从后往前扫描日志。二、设计与计算题
题目1:E-R图设计
题目: 设计一个某公司的项目管理系统的数据库。系统的主要功能是:
- 公司下设几个部门,每个部门分别有多个员工,每个员工在当前只隶属于某一部门,每个部门有一个部门负责人
- 公司同时会有多个项目同时开展,每个项目由某个部门负责,有一个项目负责人,项目组成员可来自公司的不同部门
- 一个员工同一时间只属于某个项目
- 每个项目推进过程中各个阶段产生费用的报销单据
答案:
关系模型
| 关系名 | 属性 | 主码 | 外码 |
|---|---|---|---|
| 部门 | 部门编号,部门名称,部长员工号 | 部门编号 | 部长员工号→员工 |
| 员工 | 员工编号,姓名,所在部门编号,所在项目编号 | 员工编号 | 所在部门编号→部门,所在项目编号→项目 |
| 项目 | 项目编号,名称,开始时间,结束时间,预算,主要内容,部门号 | 项目编号 | 部门号→部门 |
| 单据 | 单据号,内容,所属项目编号 | 单据号 | 所属项目编号→项目 |
评分标准: 关系模式名称,属性合理每个关系模式1分,主码,外码标注正确各1分
为什么这么写:
第一步:识别实体
从题目中提取实体:
- 部门(Department)
- 员工(Employee)
- 项目(Project)
- 单据(Document)第二步:识别关系
从题目中提取关系:
- 部门-员工:1:N(一个部门有多个员工,一个员工属于一个部门)
- 部门-项目:1:N(一个部门负责多个项目,一个项目由一个部门负责)
- 员工-项目:N:1(一个员工同一时间只属于一个项目)
- 项目-单据:1:N(一个项目有多张单据)第三步:确定主键
每个实体选择一个唯一标识符作为主键:
- 部门:部门编号
- 员工:员工编号
- 项目:项目编号
- 单据:单据号第四步:设置外键
在多端设置外键指向一端的主键:
- 员工表:所在部门编号→部门,所在项目编号→项目
- 项目表:部门号→部门
- 单据表:所属项目编号→项目涉及的知识点:
| 知识点 | 内容 |
|---|---|
| E-R图设计 | 识别实体、关系、基数 |
| 基数判断 | 1:1、1:N、M:N |
| 关系模型 | 实体转换为表,关系转换为外键 |
| 主键选择 | 每个实体选择一个唯一标识符作为主键 |
| 外键设置 | 在多端设置外键指向一端的主键 |
记忆口诀:
E-R图:矩形=实体,菱形=联系,椭圆=属性
M:N关系需要新建表
外键放在多端三、综合题
题目1:社团管理数据库
题目: 设学校社团管理数据库有如下基本表:
- 学生:S(sno, sname, gender, sage, sdept)
- 社团:T(Tno, Tname, sno)
- 社团成员:ST(sno, Tno, date)
A. 关系代数查询
1. 查询"计算机"系参加了社团名为"音乐社"的学生的学号、姓名
答案:
π_{sno,sname}(σ_{Tname='音乐社'}(T) ⋈ ST ⋈ σ_{sdept='计算机系'}(S))为什么这么写:
1. 先从 T 中选择 Tname='音乐社' 的社团
2. 从 S 中选择 sdept='计算机系' 的学生
3. 将结果与 ST 进行自然连接
4. 最后投影出 sno 和 sname知识点:
- 选择(σ):从关系中选取满足条件的元组
- 投影(π):从关系中选取指定的属性列
- 连接(⋈):按条件连接两个关系
2. 查询"计算机"系中没有参加任何社团的学生的学号
答案:
π_{sno}(σ_{sdept='计算机系'}(S)) - π_{sno}(ST)为什么这么写:
1. 先找出计算机系所有学生的学号
2. 再找出参加了社团的所有学生学号
3. 用差运算得到没有参加社团的学生学号知识点:
- 差运算(−):属于 R 但不属于 S 的元组
B. SQL 查询
1. 定义 T 表(含完整性约束)
答案:
Create table T(
Tno int,
Tname char(20),
sno char(8),
primary key(Tno),
foreign key(sno) references S(sno)
)为什么这么写:
- primary key(Tno):定义实体完整性,Tno 不能为空且唯一
- foreign key(sno) references S(sno):定义参照完整性,sno 必须在 S 表中存在知识点:
- 实体完整性:主键不能为空且唯一
- 参照完整性:外键必须在被参照表中存在
2. 查询参加人数超过50人的社团的名称
答案:
SELECT T.Tname
FROM T, ST
WHERE T.Tno = ST.Tno
GROUP BY T.Tno, T.Tname
HAVING COUNT(ST.sno) > 50为什么这么写:
1. 连接 T 和 ST 表
2. 按社团分组
3. 使用 HAVING 过滤分组后的人数知识点:
- GROUP BY:分组查询
- HAVING:分组后过滤
- COUNT:聚合函数,计算行数
3. 查询参加音乐社或者文学社的学生学号和姓名
答案:
SELECT DISTINCT S.sno, S.sname
FROM S, T, ST
WHERE T.Tno = ST.Tno
AND S.sno = ST.sno
AND (Tname = '音乐社' OR Tname = '文学社')为什么这么写:
1. 三表连接
2. 使用 OR 条件筛选两个社团
3. 使用 DISTINCT 去重(一个学生可能同时参加两个社团)知识点:
- DISTINCT:去重
- OR:逻辑或,满足任一条件即可
4. 查询"王星"同学参加的所有的社团号和社团名
答案:
SELECT T.Tno, T.Tname
FROM S, T, ST
WHERE T.Tno = ST.Tno
AND S.sno = ST.sno
AND S.sname = '王星'为什么这么写:
简单的三表连接查询,筛选条件是 sname='王星'知识点:
- 多表连接查询
- WHERE 条件筛选
5. 查询社团负责学生是计算机系的社团号和社团名
答案:
SELECT Tno, Tname
FROM T
WHERE sno IN (SELECT sno FROM S WHERE sdept = '计算机系')为什么这么写:
1. 使用子查询找出计算机系学生的学号
2. 外层查询找出这些学生负责的社团知识点:
- IN 子查询:检查值是否在子查询结果中
- 嵌套查询:一个查询嵌套在另一个查询中
6. 查询参加了学号"20010101"学生所参加的所有社团的学生学号
答案:
SELECT S.sno
FROM S
WHERE NOT EXISTS (
SELECT *
FROM ST
WHERE ST.sno = '20010101'
AND NOT EXISTS (
SELECT *
FROM ST AS ST2
WHERE S.sno = ST2.sno
AND ST.tno = ST2.tno
)
)为什么这么写:
这是经典的"查询参加了全部课程的学生"问题的变体(所有社团)。
使用双重 NOT EXISTS 实现关系代数中的除运算(÷):
1. 外层 NOT EXISTS:对于 S 中的每个学生
2. 内层 NOT EXISTS:检查 "20010101" 参加的每个社团,该学生也都参加了
3. 如果内层 NOT EXISTS 条件成立(即不存在某个社团,20010101 参加了但该学生没参加),则该学生满足条件知识点:
- NOT EXISTS:不存在
- 双重 NOT EXISTS:实现除运算("所有"问题)
- 除运算:关系代数中的除法操作
记忆口诀:
双重 NOT EXISTS = 除运算 = "所有"问题7. 新增"交响乐团"记录
答案:
INSERT INTO T(Tno, Tname) VALUES(6, '交响乐团');为什么这么写:
- sno 为空(负责学生待定),这是允许的(外键可为 NULL)知识点:
- INSERT 语句:插入新记录
- 外键可为 NULL
8. 删除"影视社团"记录(考虑完整性约束)
答案:
-- 先删除社团成员表中的相关记录
DELETE FROM ST
WHERE tno = (SELECT tno FROM T WHERE Tname = '影视社团');
-- 再删除社团表中的记录
DELETE FROM T
WHERE Tname = '影视社团';为什么这么写:
1. 由于 ST 表有外键引用 T 表,必须先删除子表(ST)记录
2. 再删除父表(T)记录
3. 顺序不能颠倒,否则会违反参照完整性约束知识点:
- DELETE 语句:删除记录
- 删除顺序:先删子表,再删父表
- 参照完整性约束
记忆口诀:
删除顺序:先删子表,再删父表题目2:范式分析与分解
题目: 设某公司管理职工每日生产产品数量的关系模式:R(职工编号,日期,日产量,车间编号,车间主任)
说明:
- 日产量为每个职工每日的产量
- 一个车间有一个车间主任
- 每个职工隶属于一个车间,一个车间有多名职工
(1)求候选码
答案: F = {(职工编号, 日期)→日产量,车间编号→车间主任,职工编号→车间编号} 码:(职工编号, 日期)
为什么这么写:
1. 属性分类:
- L类(只出现在左边):职工编号,日期
- LR类(左右都出现):车间编号
- R类(只出现在右边):日产量,车间主任
2. 计算闭包:
- (职工编号, 日期) → 日产量 ✓
- (职工编号, 日期) → 车间编号(通过 职工编号→车间编号)✓
- (职工编号, 日期) → 车间主任(通过 车间编号→车间主任)✓
3. 所以 (职工编号, 日期) 能推出所有属性,是候选码知识点:
- 候选码:能唯一标识元组的最小属性集
- 属性分类:L类、R类、LR类、N类
- 闭包计算:X⁺ = 所有能从X推出的属性集合
(2)判断最高范式
答案: R 最高为 1NF
为什么这么写:
1. 码:(职工编号, 日期)
2. 非主属性:日产量,车间编号,车间主任
3. 检查部分函数依赖:
- 职工编号 → 车间编号
- 职工编号是码的一部分,但不是整个码
- 车间编号 部分函数依赖于码
4. 由于存在非主属性对码的部分函数依赖,所以 R 只是 1NF知识点:
- 1NF:属性不可再分
- 2NF:消除非主属性对码的部分函数依赖
- 部分函数依赖:非主属性只依赖于码的一部分
记忆口诀:
1NF:属性不可再分
2NF:消除部分依赖
3NF:消除传递依赖
BCNF:决定因素是超码(3)分解为 3NF
答案: 分解为3NF 职工(职工编号,车间编号) 车间(车间编号,车间主任) 日产量(职工编号,日期,日产量)
为什么这么写:
1. 分解原则:将部分函数依赖和传递函数依赖分离
2. 职工表:存储职工和车间的隶属关系
- 职工编号 → 车间编号
- 无部分函数依赖,无传递函数依赖
3. 车间表:存储车间和车间主任的对应关系
- 车间编号 → 车间主任
- 无部分函数依赖,无传递函数依赖
4. 日产量表:存储每个职工每日的产量
- (职工编号, 日期) → 日产量
- 无部分函数依赖,无传递函数依赖知识点:
- 3NF:消除非主属性对码的传递函数依赖
- 分解原则:保持无损连接和函数依赖
题目3:两段锁与死锁
题目: 设 T1,T2 是如下 2 个事务,其中 A、B 为数据库中 2 个数据项:
- T1:如果 A>0 则更新 B=B+10
- T2:如果 B>0 则更新 A=A+5
请问使 2 事务遵守两段锁协议的情况下,是否会发生死锁。请举例说明。
答案: 有可能产生死锁。如下调度:遵循两段锁协议,并发生死锁的调度。
为什么这么写:
第一步:理解两段锁协议(2PL)
扩展阶段:只能加锁
收缩阶段:只能解锁
作用:保证并发调度的可串行性
注意:2PL可能死锁!第二步:构造死锁调度
| 时间 | T1 | T2 |
|------|----|----|
| t1 | Lock(A) | |
| t2 | Read(A) | |
| t3 | | Lock(B) |
| t4 | | Read(B) |
| t5 | Lock(B) ← 等待T2 | |
| t6 | | Lock(A) ← 等待T1 |
| | **死锁!** | **死锁!** |第三步:分析死锁原因
T1 持有 A 的锁,请求 B 的锁
T2 持有 B 的锁,请求 A 的锁
形成循环等待涉及的知识点:
| 知识点 | 内容 |
|---|---|
| 两段锁协议(2PL) | 扩展阶段只能加锁,收缩阶段只能解锁 |
| 死锁 | 循环等待 |
| 死锁四条件 | 互斥、持有并等待、不可剥夺、循环等待 |
| 死锁处理 | 预防、检测与恢复 |
记忆口诀:
2PL保证可串行化,但可能死锁
死锁四条件:互斥、持有并等待、不可剥夺、循环等待四、核心口诀汇总
完整性约束
实体完整性:主键不能为空
参照完整性:外键必须存在或为空范式判断
1NF:属性不可再分
2NF:消除部分依赖
3NF:消除传递依赖
BCNF:决定因素是超码SQL查询
双重NOT EXISTS = 除运算 = "所有"
删除顺序:先删子表,再删父表事务并发
ACID:原子一致性隔离持久
2PL:扩展阶段加锁,收缩阶段解锁
死锁四条件:互斥、持有并等待、不可剥夺、循环等待数据库恢复
已提交重做,未提交撤销
从后往前扫描日志MySQL 命令行访问
本地登录
mysql -u root -p远程登录
mysql -h IP -u root -p常用命令
- SHOW DATABASES; — 查看数据库
- USE 库名; — 进入数据库
- SHOW TABLES; — 查看表
- DESC 表名; — 查看表结构
题目知识点解析完成!祝考试顺利!🎓