一、E-R图与关系模式设计
考点本质
E-R图设计考查的是从现实世界需求到抽象数据模型的转换能力。核心在于识别实体、联系、属性三要素,并正确判断联系类型(1:1、1:n、n:m、三元联系)。
1.1 核心知识体系
实体-联系模型三要素
联系类型的本质区别
E-R图转关系模式的本质规则
核心原则:联系的语义决定转换方式
1.2 考点深挖:联系类型的判断技巧
判断口诀
"一个A对应一个B" → 1:1
"一个A对应多个B,一个B对应一个A" → 1:n
"一个A对应多个B,一个B对应多个A" → n:m
"三个实体互相独立,通过同一事件关联" → 三元联系
典型误判分析
深挖1:实体 vs 联系的区分本质
核心问题:什么时候应该设计为实体?什么时候应该设计为联系?
判断技巧:
深挖2:联系属性的归属问题
本质:联系属性属于联系本身,不属于任何一方实体。常见错误:
错误:将"成绩"放在学生表中
正确:"成绩"放在选课联系表中
错误:将"采购数量"放在供应商表中
正确:"采购数量"放在采购联系表中
判断原则:
深挖3:三元联系 vs 三个二元联系
本质区别:
判断方法:
如果 "某个A的某个B可以从某个C获取" → 三元联系
如果 "A和B有关系,B和C有关系,A和C也有关系" → 三个二元联系
示例对比:
深挖4:1:1联系的处理策略
三种处理方式:
考试技巧:如果没有特别说明,任选一方合并即可,但通常选择将可选端(0..1)合并到强制端(1..1)。
1.3 举一反三:特殊场景处理
场景1:弱实体(Weak Entity)
本质:弱实体是依赖于强实体存在的实体,自身没有完整的主键。识别特征:
典型案例:
关系模式:
员工(员工编号,姓名,...)
家属(员工编号,家属姓名,关系,出生日期)
↑外键参照员工表
场景2:自反联系(Recursive Relationship)
本质:同一实体集内部的联系。典型案例:
关系模式:
员工(员工编号,姓名,领导编号)
↑外键参照本表员工编号
场景3:多值属性(Multivalued Attribute)
本质:一个实体可以有多个相同类型的属性值。识别特征:
处理方式:将多值属性独立成表典型案例:
学生(学号,姓名)
兴趣爱好(学号,兴趣爱好)
↑联合主键
场景4:三元联系的识别与处理
本质:三个实体之间的关联,不能拆分为两两之间的联系。识别技巧:
典型案例:供应商-零件-车型采购系统
采购(车型编号,供应商名称,零件编码,采购数量,采购日期)
↑三方主键联合
1.4 真题映射(考点落地)
【2023年-汽车零件采购系统】
- 考点
- 深挖:为什么不是三个两两联系?因为"某个车型的某种零件可以从多家供应商采购",车型、零件、供应商三者是互相独立的,必须通过三元联系才能表达这种语义。
【2022年-疫苗接种系统】
- 考点
- 深挖:被接种者与医院是n:m,联系属性"接种日期"属于联系本身,不属于任何一方实体。
【2021年-社区蔬菜团购网站】
- 考点
- 深挖:订单本身有独立属性(订单号、日期),所以订单应设计为实体,而非客户与团购点之间的简单联系。
1.5 答题模板
模板1:E-R图补全题
步骤1:识别已有实体和联系
步骤2:分析需求中未表示的实体间关系
步骤3:判断联系类型(1:1、1:n、n:m)
步骤4:确定联系属性(如有)
步骤5:用标准符号补全E-R图
模板2:关系模式补全题
步骤1:确定联系类型
步骤2:n:m联系→单独成表,双方主键+联系属性
步骤3:1:n联系→将1方主键加入n方
步骤4:标注主键(下划线)和外键(→参照)
二、SQL语句补充
考点本质
SQL语句补充考查的是用结构化查询语言表达数据操作的能力。核心在于理解DDL(数据定义)和DML(数据操纵)的语法细节,特别是完整性约束的定义和复杂查询的构建。
2.1 核心知识体系
DDL的本质:定义数据的"规则"
CREATE TABLE不仅是创建表,更是定义数据的完整性规则:
| | | |
|---|
| PRIMARY KEY | | | |
| UNIQUE | | | |
| REFERENCES | | | |
| ON DELETE CASCADE | | | |
| NOT NULL | | | |
| CHECK | | | |
| DEFAULT | | | |
DML的本质:从数据中提取信息
复杂查询的构建逻辑:
查询目标 → 需要哪些表 → 表间如何关联 → 筛选条件 → 分组聚合 → 排序输出
2.2 考点深挖:易混淆概念辨析
深挖1:PRIMARY KEY vs UNIQUE
考试陷阱:
-- 错误:以为UNIQUE会自动NOT NULL
Cname CHAR(30) UNIQUE -- Cname可以为NULL
-- 正确:如果需要非空+唯一
Cname CHAR(30) NOT NULL UNIQUE
深挖2:外键的两种写法
| | |
|---|
| 简写 | Sno CHAR(10) REFERENCES Student(Sno) | |
| 完整 | FOREIGN KEY (Sno) REFERENCES Student(Sno) | |
考试陷阱:
-- 错误:联合外键不能用简写
PRIMARY KEY (Sno, Cno) -- 这是表级约束
-- 正确:联合外键必须用表级约束
FOREIGN KEY (Sno) REFERENCES Student(Sno)
深挖3:WHERE vs HAVING
记忆口诀:WHERE筛行,HAVING筛组;聚合条件只能用HAVING。
深挖4:IN vs EXISTS
经典应用:"选修了全部课程的学生"
-- 双重NOT EXISTS:不存在一门课程该学生没有选修
SELECT Sname FROM Student S
WHERE NOT EXISTS (
SELECT * FROM Course C
WHERE NOT EXISTS (
SELECT * FROM SC
WHERE SC.Sno = S.Sno AND SC.Cno = C.Cno
)
);
深挖5:连接类型的本质区别
| | | |
|---|
| INNER JOIN | | | |
| LEFT JOIN | | | |
| RIGHT JOIN | | | |
| FULL JOIN | | | |
| CROSS JOIN | | | |
记忆技巧:
深挖6:NULL值的陷阱
NULL的本质:未知值、不存在值、未定义值
| | |
|---|
NULL = NULL | | |
NULL <> NULL | | |
5 + NULL | | |
COUNT(*) | | |
COUNT(列) | | |
AVG(列) | | |
考试陷阱:
-- 错误:以为这样可以筛选NULL
WHERE 列 = NULL -- 永远返回FALSE
-- 正确:必须使用IS NULL
WHERE 列 IS NULL
-- 错误:以为NOT IN会包含NULL
WHERE 列 NOT IN (1, 2, NULL) -- 永远返回FALSE
-- 正确:排除NULL后再判断
WHERE 列 IS NOT NULL AND 列 NOT IN (1, 2)
深挖7:聚合函数与空值
| | |
|---|
| COUNT(*) | | |
| COUNT(列) | | |
| SUM(列) | | |
| AVG(列) | | |
| MAX/MIN | | |
注意:SUM和AVG忽略NULL,但COUNT(列)也忽略NULL,这是容易混淆的地方。
深挖8:SQL执行顺序(理解查询的本质)
SELECT DISTINCT 列, 聚合函数 -- 5. 选择列,去重
FROM 表A JOIN 表B ON 条件 -- 1. 先连接表
WHERE 行筛选条件 -- 2. 筛选行
GROUP BY 分组列 -- 3. 分组
HAVING 组筛选条件 -- 4. 筛选组
ORDER BY 排序列 -- 6. 排序
LIMIT n; -- 7. 限制结果数
理解这个顺序的重要性:
- WHERE不能用聚合函数(因为聚合在GROUP BY之后)
- SELECT中定义的别名不能在WHERE中使用(因为SELECT在WHERE之后执行)
- HAVING可以用SELECT中的别名(因为HAVING在SELECT之后执行)
2.3 举一反三:SQL语句变式训练
变式1:自连接的不同写法
场景:查询每门课程的间接先修课
-- 写法1:显式JOIN
SELECT K1.Cno, K2.Cpno
FROM COURSE K1 LEFT OUTER JOIN COURSE K2
ON K1.Cpno = K2.Cno;
-- 写法2:隐式连接(WHERE)
SELECT K1.Cno, K2.Cpno
FROM COURSE K1, COURSE K2
WHERE K1.Cpno = K2.Cno;
-- 考点:LEFT OUTER JOIN保留NULL,隐式连接不保留
变式2:批量插入的多种方式
-- 方式1:为所有学生插入一门新课程
INSERT INTO SC(Sno, Cno)
SELECT Sno, 'C036'
FROM STUDENT;
-- 方式2:从另一表复制数据
INSERT INTO SC(Sno, Cno)
SELECT Sno, Cno FROM SC_TEMP;
-- 方式3:插入常量值
INSERT INTO SC(Sno, Cno)
SELECT Sno, 'C036'
FROM STUDENT
WHERE Sdept = '计算机';
变式3:视图的多层嵌套
-- 基础视图:已售商品统计
CREATE VIEW GOODS_SOLD AS
SELECT Gno, COUNT(*) AS Onum, SUM(Onumber) AS Total
FROM ORDERS
GROUP BY Gno;
-- 上层视图:结合商品信息
CREATE VIEW GOODS_REPORT AS
SELECT G.Gno, G.Gname, S.Onum, S.Total
FROM GOODS G, GOODS_SOLD S
WHERE G.Gno = S.Gno;
2.4 真题映射(考点落地)
【2023年-教务管理系统】
- 考点
- 深挖:课程表的先修课参照本表,这是自反联系在SQL中的实现。注意REFERENCES后面是
COURSE(Cno),不是Cpno。
【2022年-工程项目管理系统】
- 考点
- 深挖:CHECK不仅是范围检查,还可以是枚举检查(
CHECK (Rank IN ('一','二','三','无')))。
【2021年-竞赛管理系统】
- 考点
- 深挖:联合主键的语法是
PRIMARY KEY (Pno, Cno),不是PRIMARY KEY Pno, Cno。
2.5 答题模板
模板1:CREATE TABLE完整框架
CREATE TABLE 表名(
列1 类型 [约束], -- 主键列
列2 类型 REFERENCES 父表(列), -- 外键列(简写)
列3 类型 NOT NULL, -- 非空列
列4 类型 UNIQUE, -- 唯一列
列5 类型 CHECK(条件), -- 检查约束
列6 类型 DEFAULT '值', -- 默认值
[表级约束]
);
模板2:复杂查询构建步骤
-- 步骤1:确定目标列
SELECT 列1, 列2, 聚合函数
-- 步骤2:确定数据源
FROM 表A [别名], 表B [别名]
-- 步骤3:建立表间关联(n个表需要n-1个条件)
WHERE A.外键 = B.主键
-- 步骤4:行级筛选
AND 筛选条件
-- 步骤5:分组
GROUP BY 分组列
-- 步骤6:组级筛选
HAVING 组条件
-- 步骤7:排序
ORDER BY 排序列 [DESC];
三、存储过程与触发器
考点本质
存储过程考查封装业务逻辑的能力,触发器考查自动响应数据变化的能力。两者都是数据库编程的核心,区别在于:存储过程是"主动调用",触发器是"被动响应"。
3.1 核心知识体系
存储过程的本质:封装+复用+安全
触发器的本质:事件驱动编程
NEW vs OLD 的本质
3.2 考点深挖:存储过程的关键技术
深挖1:游标的本质与使用场景
本质:游标是逐行处理查询结果的机制,类似于编程语言中的迭代器。使用场景:
标准流程:
DECLARE CURSOR 游标名 IS SELECT语句; -- 声明:定义查询
OPEN 游标名; -- 打开:执行查询
FETCH 游标名 INTO 变量列表; -- 取数据:逐行读取
-- 循环处理
CLOSE 游标名; -- 关闭:释放资源
考试陷阱:
-- 错误:忘记判断游标结束
LOOP
FETCH c INTO v; -- 最后一次FETCH失败,v保留上一行值
INSERT INTO ...; -- 会多插入一条重复记录
END LOOP;
-- 正确:先判断再处理
LOOP
FETCH c INTO v;
EXIT WHEN c%NOTFOUND; -- 先判断
INSERT INTO ...; -- 再处理
END LOOP;
深挖2:事务控制的本质
本质:事务是保证数据一致性的边界。
BEGIN
-- 操作1
-- 操作2
COMMIT; -- 全部成功,永久生效
EXCEPTION WHEN OTHERS THEN
ROLLBACK; -- 任何失败,全部撤销
END;
考试重点:
- 必须有
EXCEPTION WHEN OTHERS THEN ROLLBACK
深挖3:参数模式的本质
深挖4:游标 vs 批量操作的对比
核心原则:能用批量操作就不用游标。考试中出现游标,通常是因为每行需要不同的处理逻辑。
深挖5:FOR UPDATE的本质
本质:在SELECT时加排他锁(X锁),防止其他事务同时修改。
SELECT ... INTO ... FROM 表 WHERE ... FOR UPDATE;
作用:
注意:FOR UPDATE只在事务中有效,事务提交或回滚后锁自动释放。
深挖6:存储过程中的异常处理
| | |
|---|
| NO_DATA_FOUND | | |
| TOO_MANY_ROWS | | |
| OTHERS | | |
标准异常处理结构:
BEGIN
-- 业务逻辑
COMMIT;
EXCEPTION
WHEN NO_DATA_FOUND THEN
-- 处理无数据情况
ROLLBACK;
WHEN TOO_MANY_ROWS THEN
-- 处理多行情况
ROLLBACK;
WHEN OTHERS THEN
-- 处理其他异常
ROLLBACK;
END;
3.3 考点深挖:触发器的关键技术
深挖1:BEFORE vs AFTER 的选择
本质原因:BEFORE时事务还未提交,修改其他表可能导致不一致;AFTER时当前操作已完成,可以安全地操作其他表。
深挖2:行级 vs 语句级触发器
| | | |
|---|
| FOR EACH ROW | | | |
| FOR EACH STATEMENT | | | |
考试重点:需要访问NEW.列名或OLD.列名时,必须用FOR EACH ROW。
深挖3:触发器中的条件判断
-- 只在库存从高于最低值变为低于最低值时触发
WHEN NEW.all_nums < (SELECT level FROM bookminlevel WHERE ...)
AND OLD.all_nums >= (SELECT level FROM bookminlevel WHERE ...)
本质:通过比较NEW和OLD,精确控制触发时机,避免不必要的触发。
深挖4:触发器与存储过程的对比
核心区别:触发器是"事件驱动",存储过程是"命令驱动"。
深挖5:触发器的执行顺序
当多个触发器作用于同一表时,执行顺序为:
BEFORE STATEMENT触发器
→ BEFORE ROW触发器(每行)
→ 执行DML操作
→ AFTER ROW触发器(每行)
→ AFTER STATEMENT触发器
考试要点:
- 同一事件多个触发器,执行顺序不确定(依赖具体DBMS)
深挖6:触发器的限制与注意事项
| | |
|---|
| 不能显式提交/回滚 | | |
| 不能修改触发表 | | 在AFTER UPDATE触发器中再次UPDATE本表 |
| 递归触发 | | |
| 性能影响 | | |
记忆口诀:触发器三不能——不能提交、不能回滚、不能改本表(行级)。
3.4 举一反三:存储过程变式
变式1:带OUT参数的存储过程
CREATE PROCEDURE GetStats(
IN deptId CHAR(10),
OUT empCount INT,
OUT avgSalary DECIMAL(10,2)
)
BEGIN
SELECT COUNT(*), AVG(salary)
INTO empCount, avgSalary
FROM employees
WHERE dept_id = deptId;
END;
变式2:动态SQL的存储过程
CREATE PROCEDURE DynamicQuery(IN tableName VARCHAR(50))
BEGIN
-- 注意:实际考试中较少涉及,了解即可
SET @sql = CONCAT('SELECT * FROM ', tableName);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END;
3.5 举一反三:触发器变式
变式1:多事件触发器
CREATE TRIGGER AuditTrigger
AFTER INSERT OR UPDATE OR DELETE ON employees
FOR EACH ROW
BEGIN
IF INSERTING THEN
INSERT INTO audit_log VALUES ('INSERT', NEW.id, SYSDATE);
ELSIF UPDATING THEN
INSERT INTO audit_log VALUES ('UPDATE', NEW.id, SYSDATE);
ELSIF DELETING THEN
INSERT INTO audit_log VALUES ('DELETE', OLD.id, SYSDATE);
END IF;
END;
变式2:级联删除触发器
CREATE TRIGGER CascadeDelete
BEFORE DELETE ON departments
FOR EACH ROW
BEGIN
-- 删除部门前,先删除该部门的员工
DELETE FROM employees WHERE dept_id = OLD.dept_id;
END;
3.6 真题映射(考点落地)
【2023年-工资计算系统】
- 考点:存储过程(参数模式+SELECT INTO+事务控制)
- 深挖:
FOR UPDATE的作用是在读取时加X锁,防止并发修改导致数据不一致。
【2022年-银行账务系统】
- 考点
- 深挖:游标适用于"逐行处理且每行逻辑不同"的场景。如果每行处理逻辑相同,可以用INSERT INTO SELECT批量完成。
【2021年-网上书城系统】
- 考点
- 深挖:
WHEN NEW.all_nums < ... AND OLD.all_nums >= ...精确控制只在"库存从高于最低值变为低于最低值"时触发,避免重复触发。
3.7 答题模板
模板1:存储过程(带游标)
CREATE PROCEDURE 过程名(IN 参数 类型)
DECLARE
变量1 类型;
变量2 类型;
CURSOR 游标名 IS SELECT语句;
BEGIN
OPEN 游标名;
LOOP
FETCH 游标名 INTO 变量1, 变量2;
EXIT WHEN 游标名%NOTFOUND; -- 先判断结束
-- 业务逻辑
END LOOP;
CLOSE 游标名;
COMMIT;
EXCEPTION WHEN OTHERS THEN
ROLLBACK;
END;
模板2:BEFORE触发器(数据验证)
CREATE TRIGGER 触发器名
BEFORE INSERT OR UPDATE ON 表名
FOR EACH ROW
BEGIN
IF NEW.列 < 0 THEN
Raise_Error; -- 抛出异常,阻止操作
END IF;
END;
模板3:AFTER触发器(级联操作)
CREATE TRIGGER 触发器名
AFTER INSERT OR DELETE OR UPDATE ON 表名
FOR EACH ROW
BEGIN
IF INSERTING THEN
UPDATE 另一表 SET 列 = 列 + 1 WHERE ...;
ELSIF DELETING THEN
UPDATE 另一表 SET 列 = 列 - 1 WHERE ...;
END IF;
END;
四、事务并发控制
考点本质
事务并发控制考查的是多用户同时访问数据库时如何保证数据一致性的能力。核心在于理解并发操作导致的数据不一致性问题,以及如何通过封锁机制和隔离级别解决这些问题。
4.1 核心知识体系
事务的ACID本质
并发问题的本质:操作的交错执行
核心思想:如果事务串行执行(一个做完再做另一个),不会出现问题。问题源于操作的交错执行。
4.2 考点深挖:封锁机制
深挖1:S锁与X锁的本质
兼容性矩阵:
深挖2:封锁协议的层级
记忆口诀:
一级改:只解决丢失更新
二级读:解决脏读(读完就放)
三级锁:解决不可重复读(事务结束才放)
深挖3:2PL协议与死锁
2PL协议的本质:
- 扩展阶段(Growing Phase):只能加锁,不能解锁
- 收缩阶段(Shrinking Phase):只能解锁,不能加锁
2PL能保证什么:
死锁的本质:循环等待
T1: XLock(A) → 请求 XLock(B) → 等待T2
T2: XLock(B) → 请求 XLock(A) → 等待T1
结果:死锁!
死锁解决方案:
4.3 考点深挖:隔离级别
隔离级别的本质:性能与一致性的权衡
关键理解:
深挖4:隔离级别的实现机制
本质理解:
- READ COMMITTED:S锁只保护单次读取,不保护整个事务
- REPEATABLE READ:S锁保护整个事务,防止其他事务修改已读数据
- SERIALIZABLE:不仅保护已读数据,还防止新数据插入(幻读)
深挖5:MVCC(多版本并发控制)
本质:通过保存数据的多个版本,实现读写不阻塞。核心思想:
与封锁的对比:
考试要点:
4.4 举一反三:并发场景分析
场景1:丢失更新(库存扣减)
T1: read(A)=100
T2: read(A)=100
T1: A=A-10=90, write(A)=90
T2: A=A-20=80, write(A)=80 -- T1的更新丢失!
正确结果:应该是 100 - 10 - 20 = 70实际结果:A = 80(T1的-10丢失了)解决方案:
T1: XLock(A), read(A)=100, A=90, write(A), Unlock(A)
T2: XLock(A) -- 等待T1释放锁
场景2:脏读(转账回滚)
T1: read(A)=1000, A=900, write(A)=900
T2: read(A)=900 -- 脏读!T1还未提交
T1: ROLLBACK -- A恢复为1000
结果:T2读取了不存在的数据900解决方案:
T1: XLock(A), read(A), A=900, write(A)...
T2: SLock(A) -- 等待T1释放X锁
场景3:不可重复读(统计报表)
T1: read(A)=100
T2: read(A)=100, A=150, write(A)=150, COMMIT
T1: read(A)=150 -- 同一事务两次读取不同!
解决方案:
T1: SLock(A), read(A)=100...
T2: XLock(A) -- 等待T1释放S锁
4.5 真题映射(考点落地)
【2023年-工资计算系统】
- 考点
- 深挖:工资计算事务和奖罚事务同时修改同一员工的工资记录,后提交的覆盖先提交的。2PL可以解决,但可能产生死锁。
【2021年-网上书城系统】
- 考点
- 深挖:用户1修改库存后回滚,用户2读取了未提交的修改并基于此计算,导致数据不一致。
【2022年-银行账务系统】
- 考点
- 深挖:提交读隔离级别下,汇总事务读取已提交的数据;MVCC通过快照读避免读写冲突。
4.6 答题模板
模板1:并发问题分析
步骤1:画出时间线,列出每个事务的操作
步骤2:跟踪数据项的值变化
步骤3:判断是否存在以下情况:
- 两个事务都读同一数据,然后先后写 → 丢失更新
- 读取了未提交的数据 → 脏读
- 同一事务内两次读取结果不同 → 不可重复读
- 同一事务内两次查询记录数不同 → 幻读
步骤4:给出正确结果和实际结果,分析差异
模板2:并发问题解决方案
方案1:加锁(XLock/SLock)
方案2:提高隔离级别
方案3:使用MVCC(多版本并发控制)
方案4:乐观并发控制(提交时检查冲突)
五、数据库故障恢复
考点本质
数据库故障恢复考查的是在系统发生故障后如何保证数据一致性和持久性的能力。核心在于理解日志机制、检查点技术和Redo/Undo恢复算法。
5.1 核心知识体系
故障类型的本质区别
日志的本质:数据库的"黑匣子"
日志记录了所有对数据库的修改操作,是恢复的唯一依据。
| | |
|---|
<Ti, START> | | |
<Ti, COMMIT> | | |
<Ti, D, V1, V2> | | |
<Ti, abort> | | |
CHECKPOINT | | |
5.2 考点深挖:检查点恢复算法
深挖1:检查点的本质
本质:检查点是数据库的一个一致性快照点,用于减少恢复时需要扫描的日志量。检查点的工作:
深挖2:Redo/Undo的本质
为什么Redo正向,Undo反向?
- Redo正向:一个数据项可能被多个事务修改,正向扫描保证最终值是最后一个已提交事务的值
- Undo反向:一个未提交事务可能修改了多个数据项,反向扫描保证按修改的逆序撤销
深挖3:WAL原则(Write Ahead Logging)
本质:先写日志,后写数据库。为什么必须先写日志?
错误顺序(先写数据库):
1. T1修改A: A=100→200(写入数据库)
2. 系统崩溃(日志未写入)
3. 恢复时:没有T1的日志记录
4. 结果:T1的修改永久生效(即使T1未提交)
正确顺序(先写日志):
1. T1写入日志: <T1,A,100,200>
2. T1修改A: A=100→200(写入数据库)
3. 系统崩溃
4. 恢复时:根据日志Redo/Undo
深挖4:检查点的分类与对比
考试重点:模糊检查点是最常用的,恢复时需要Redo检查点后已提交的事务。
深挖5:日志记录格式的本质
| | | |
|---|
| 物理日志 | <Ti, 页号, 偏移量, 旧值, 新值> | | |
| 逻辑日志 | <Ti, SQL语句> | | |
| 物理逻辑日志 | <Ti, 记录标识, 旧值, 新值> | | |
考试要点:
深挖6:ARIES恢复算法(进阶)
核心思想:
与简单恢复算法的区别:
考试要点:了解ARIES的三个阶段即可,不需要深入细节。
深挖7:备份策略的本质
备份策略选择:
5.3 举一反三:恢复场景分析
场景1:系统故障恢复
日志:
LSN1: <T1, START>
LSN2: <T1, A, 10, 20>
LSN3: <T2, START>
LSN4: <T2, B, 30, 40>
LSN5: <T1, COMMIT>
LSN6: CHECKPOINT
LSN7: <T2, A, 20, 50>
LSN8: <T3, START>
LSN9: <T3, B, 40, 60>
LSN10: CRASH
分析:
Redo(正向):
Undo(反向):
结果:A = 20, B = 30
场景2:介质故障恢复
恢复步骤:
5. 验证数据一致性与系统故障的区别:
5.4 真题映射(考点落地)
【2022年-数据库故障恢复】
- 考点
- 深挖:CHECKPOINT前已提交的事务(T1)也需要Redo,因为检查点时不保证所有修改都已刷盘。
【2020年-数据库故障恢复】
- 考点
- 深挖:如果先写数据库再记日志,已提交的事务可能因为COMMIT日志未写入而被错误地Undo。
5.5 答题模板
模板1:恢复过程分析
步骤1:从事务状态判断Redo/Undo列表
- 有COMMIT → Redo
- 无COMMIT → Undo
步骤2:Redo阶段(正向扫描)
- 从CHECKPOINT开始正向扫描
- 对已提交事务,按日志顺序重做修改
步骤3:Undo阶段(反向扫描)
- 从CRASH点反向扫描
- 对未提交事务,按逆序撤销修改
步骤4:计算最终结果
- 初始值 + 已提交修改 - 未提交修改
模板2:介质故障恢复
1. 修复硬件(更换磁盘)
2. 重装DBMS
3. 载入最近备份
4. 扫描日志:
- Redo:备份后已提交的事务
- Undo:备份后未提交的事务
5. 验证一致性
六、范式与关系模式规范化
考点本质
范式考查的是如何设计没有数据冗余和异常的关系模式。核心在于理解函数依赖、识别冗余来源,并掌握模式分解的方法。
6.1 核心知识体系
函数依赖的本质:属性间的决定关系
范式的本质:逐步消除冗余
递进关系:
1NF ⊃ 2NF ⊃ 3NF ⊃ BCNF ⊃ 4NF
(越往后约束越强,冗余越少)
6.2 考点深挖:模式分解
深挖1:分解的目标
核心目标:
- 无损连接性
- 保持函数依赖
考试重点:通常要求分解到3NF或BCNF,且保持函数依赖。
深挖2:部分依赖的分解
识别:非主属性只依赖于主键的一部分。分解方法:将部分依赖的属性分离成新表。示例:
原表:选课(学号,课程号,姓名,成绩)
主键:(学号,课程号)
部分依赖:学号 → 姓名
分解:
1. 选课(学号,课程号,成绩)
2. 学生(学号,姓名)
深挖3:传递依赖的分解
识别:非主属性通过另一个非主属性依赖于主键。分解方法:将传递依赖的中间属性分离成新表。示例:
原表:学生(学号,姓名,系号,系主任)
传递依赖:学号 → 系号 → 系主任
分解:
1. 学生(学号,姓名,系号)
2. 系(系号,系主任)
深挖4:多值依赖与4NF
本质:一个属性值对应另一组属性的多个值,且这两组属性互相独立。识别:
病例(编号,药品条码,患者,医生,病情,诊断,日期,剂量,数量)
编号 →→ 药品, 剂量, 数量(多值依赖)
分解:
1. 病例基本信息(编号,患者,医生,病情,诊断,日期)
2. 病例药品信息(编号,药品条码,剂量,数量)
深挖5:候选键的求解方法
本质:候选键是能唯一确定所有属性的最小属性集。求解步骤:
- 找L类属性
- 找R类属性
- 找LR类属性
- 找N类属性
判断方法:
- 如果L类和N类属性的闭包包含所有属性 → 这就是候选键
- 如果不包含,逐个加入LR类属性,直到闭包包含所有属性
示例:
关系R(A, B, C, D, E)
函数依赖:A→B, B→C, C→D, D→E
分析:
- L类:A
- R类:E
- LR类:B, C, D
- N类:无
A的闭包:A → B → C → D → E
包含所有属性,所以候选键是{A}
深挖6:无损连接性的判断
本质:分解后能通过自然连接恢复原始关系。判断方法(表格法):
- 构造一个表格,行是分解后的关系模式,列是原始关系的所有属性
示例:
R(A, B, C), F = {A→B}
分解为:R1(A, B), R2(A, C)
表格:
A B C
R1 a a b
R2 a b a
根据A→B:R1和R2的A都是a,所以R2的B应该等于R1的B
修改后:
A B C
R1 a a b
R2 a a a ← 全为a,无损!
深挖7:函数依赖保持性的判断
本质:分解后的每个函数依赖都能在某一个分解后的关系模式上验证。判断方法:
- 对每个函数依赖X→Y,检查X和Y是否都在同一个分解后的关系模式中
注意:无损连接性和函数依赖保持性是独立的,一个分解可以无损但不保持函数依赖,也可以保持函数依赖但有损。
深挖8:BCNF vs 3NF的选择
选择原则:
6.3 举一反三:范式判断练习
练习1:判断2NF
关系:选课(学号,课程号,姓名,课程名,成绩)
主键:(学号,课程号)
分析:
- 学号 → 姓名(部分依赖)
- 课程号 → 课程名(部分依赖)
结论:不满足2NF
练习2:判断3NF
关系:学生(学号,姓名,系号,系主任)
主键:学号
分析:
- 学号 → 系号(直接依赖)
- 系号 → 系主任(传递依赖)
- 学号 → 系主任(传递依赖)
结论:不满足3NF
练习3:判断BCNF
关系:选课(学生,课程,教师)
假设:每个教师只教一门课,一门课有多个教师
函数依赖:
- (学生,课程)→ 教师
- 教师 → 课程
分析:
- 候选键:(学生,课程)、(学生,教师)
- 教师 → 课程:决定因素"教师"不是候选键
结论:不满足BCNF
6.4 真题映射(考点落地)
【2023年-维修配件管理系统】
- 考点
- 深挖:识别出配件编码→配件名称、配件编码→配件供应商等依赖,配件编码不是超键,所以不满足BCNF。
【2019年-快递跟踪管理系统】
- 考点
- 深挖:快递编号→发件人身份证号→发件人姓名/电话/地址,存在传递依赖,分解为两个关系。
【2018年-医院信息管理系统】
- 考点
- 深挖:病例编号→→药品/剂量/数量是多值依赖,一个病例对应多个药品,且药品之间互相独立。
6.5 答题模板
模板1:范式判断
步骤1:确定候选键(能唯一确定所有属性的最小属性集)
步骤2:找出所有函数依赖
步骤3:判断是否满足各范式:
- 1NF:属性是否可再分?
- 2NF:是否存在部分依赖?
- 3NF:是否存在传递依赖?
- BCNF:每个决定因素是否都是候选键?
步骤4:如不满足,说明理由
模板2:模式分解
步骤1:找出违反范式的函数依赖
步骤2:将违反依赖的属性分离成新表
步骤3:确保分解后的每个关系都满足目标范式
步骤4:标注主键(下划线)和外键(→参照)
步骤5:验证无损连接性和函数依赖保持性
模板3:数据异常分析
插入异常:
- 问题:无法插入某些信息
- 原因:缺少主键的一部分
删除异常:
- 问题:删除某条记录时丢失其他信息
- 原因:不同实体的信息混在一起
修改异常:
- 问题:修改某属性时需要修改多条记录
- 原因:数据冗余
附录:案例题答题通用技巧
技巧1:审题三步法
第一步:读说明,识别实体、属性、联系
第二步:读问题,明确考查的知识点
第三步:回看说明,提取解题所需信息
技巧2:E-R图题答题要点
1. 实体用矩形,属性用椭圆,联系用菱形
2. 联系类型标注在连线旁(1:1、1:n、n:m)
3. n:m联系必须单独成表
4. 联系属性属于联系,不属于实体
技巧3:SQL题答题要点
1. CREATE TABLE:先主键,再外键,最后其他约束
2. 多表连接:n个表需要n-1个连接条件
3. 分组统计:SELECT中的非聚合列必须出现在GROUP BY中
4. 子查询:NOT IN用于"不在...中",EXISTS用于"存在..."
技巧4:存储过程/触发器题答题要点
1. 存储过程:参数模式(IN/OUT)、游标(声明-打开-取数据-关闭)、事务控制(COMMIT/ROLLBACK)
2. 触发器:BEFORE/AFTER、INSERT/UPDATE/DELETE、FOR EACH ROW、NEW/OLD
3. 注意语法细节:分号、END IF、LOOP/EXIT
技巧5:并发控制题答题要点
1. 画出时间线表格
2. 跟踪每个事务的变量值和数据库值
3. 判断问题类型:丢失更新/脏读/不可重复读/幻读
4. 解决方案:加锁(XLock/SLock)、提高隔离级别
技巧6:故障恢复题答题要点
1. 事务状态:有COMMIT→Redo,无COMMIT→Undo
2. Redo正向扫描,Undo反向扫描
3. 注意多个事务修改同一数据项的情况
4. 检查点减少恢复扫描范围
技巧7:范式题答题要点
1. 先找候选键
2. 再找函数依赖
3. 判断违反哪个范式
4. 分解时保持函数依赖和无损连接
5. 标注主键和外键
复习建议:不要死记硬背真题答案,要理解每个考点背后的原理。通过"考点本质→深挖细节→举一反三→真题映射"的学习路径,建立完整的知识体系,才能在考试中灵活应对各种变式题目。
复习建议
理解考点本质 > 死记硬背真题
通过"考点本质→深挖细节→举一反三→真题映射"的学习路径,建立完整的知识体系