LanFa的个人主页

Back

可惜事已至此,复习不完也要复习完了。。。所以————————————

《数据库系统原理》常用知识#

怎么快速判断一个关系最高到哪个范式#

可以用一个很快的判断流程。考试时不要从定义硬背,按这个顺序走:

先求候选码
再分主属性/非主属性
再看函数依赖左边是不是码
最后看多值依赖
text

第一步:先找候选码

这是所有范式判断的基础。

做法:

  1. 出现在右边的属性,通常不必一开始放进码。
  2. 从来不出现在右边的属性,通常必须在候选码里。
  3. 用属性闭包 X+ 看能不能推出全部属性。
  4. 能推出全部属性,并且去掉任何一个属性后都不能推出全部属性,就是候选码。

例如:

R(A,B,C,D)
F = {A -> B, C -> D}
text

右边出现过:

B, D
text

没出现在右边:

A, C
text

所以先试:

(AC)+
text

有:

A -> B
C -> D
text

所以:

(AC)+ = ABCD
text

因此候选码是:

AC
text

第二步:分主属性和非主属性

主属性:出现在某个候选码中的属性
非主属性:不出现在任何候选码中的属性
text

刚才例子里候选码是:

AC
text

所以:

主属性:A, C
非主属性:B, D
text

这一步很重要,因为 2NF、3NF 都要看非主属性。


第三步:判断 1NF

1NF 最简单:

属性值不可再分,每个单元格只能放一个值。

如果表里有这种东西:

学生     电话
张三     123, 456
text

一个格子里放多个电话号码,就不满足 1NF。

一般教材题默认已经满足 1NF,除非题目明显给出“多值属性”。


第四步:判断 2NF

2NF 要求:

非主属性不能部分依赖于候选码。

只在候选码是组合码时容易出问题。

判断口诀:

如果候选码是单属性码,一定满足 2NF。
如果候选码是组合码,就看有没有“候选码的一部分 -> 非主属性”。
text

例如:

R(A,B,C,D)
候选码:AC
F = {A -> B, C -> D}
text

有:

A -> B
text

其中 A 是候选码 AC 的一部分,B 是非主属性。

所以:

B 部分依赖于候选码 AC
text

违反 2NF。

因此这个关系最高只能到:

1NF
text

第五步:判断 3NF

3NF 要求:

非主属性不能传递依赖于候选码。

常见违反形式:

候选码 -> 非主属性1 -> 非主属性2
text

也就是:

X 是候选码
X -> A
A -> B
text

其中 AB 都是非主属性。

例如:

R(Sno, Sdept, Mname)
F = {Sno -> Sdept, Sdept -> Mname}
text

候选码:

Sno
text

有:

Sno -> Sdept -> Mname
text

所以 Mname 传递依赖于 Sno,违反 3NF。

这个关系如果没有部分依赖,那就是:

最高 2NF
text

更快的 3NF 判断公式是:

对于每个非平凡函数依赖:

X -> A
text

至少满足一个:

X 是超码
A 是主属性
text

否则就不满足 3NF。


第六步:判断 BCNF

BCNF 比 3NF 更严格。

BCNF 要求:

对每个非平凡函数依赖 X -> Y,X 必须是超码。

口诀:

只要有一个非平凡函数依赖的左边不是超码,就不是 BCNF。
text

例如:

R(A,B,C)
F = {AB -> C, C -> B}
text

候选码可能是:

AB, AC
text

所以主属性:

A, B, C
text

看依赖:

C -> B
text

右边 B 是主属性,所以它满足 3NF。

但是 C 不能推出全部属性,不是超码。

所以:

不满足 BCNF
text

因此这个关系:

最高 3NF
text

这也是 3NF 和 BCNF 的典型区别。


第七步:判断 4NF

4NF 是在 BCNF 基础上进一步看多值依赖。

4NF 要求:

对每个非平凡多值依赖 X ->-> Y,X 必须是超码。

口诀:

函数依赖看 BCNF
多值依赖看 4NF
text

如果有:

X ->-> Y
text

并且:

Y 不包含于 X
X ∪ Y 不是全部属性
X 不是超码
text

那么就违反 4NF。

典型例子:

Teaching(C, T, B)
text

含义:

C = 课程
T = 教师
B = 教材
text

一门课程有多个教师,也有多本教材,教师和教材独立。

存在:

C ->-> T
C ->-> B
text

但是 C 不是候选码,所以违反 4NF。

因此要拆成:

CT
CB
text

你可以直接背这个快速判断表:

考试最快流程:

1. 求候选码
2. 标主属性、非主属性
3. 看有没有部分依赖
   有:最高 1NF
4. 看有没有传递依赖
   有:最高 2NF
5. 看每个函数依赖左边是不是超码
   不是:最高 3NF
6. 看有没有非平凡多值依赖且左边不是超码
   有:最高 BCNF
7. 都没有:4NF
text

注意最后一句:

“最高到哪个范式”就是从低往高查,第一次违反的前一级,就是最高范式。

达到BCNF的无损连接分解算法#

这页讲的是:如何把一个关系模式分解到 BCNF,并且保证无损连接

核心思想很简单:

只要某个关系模式不满足 BCNF,就找出一个违反 BCNF 的函数依赖,用它把表拆成两张表。反复拆,直到所有表都满足 BCNF。


设原关系模式是:

R<U, F>
text

其中:

U = 属性全集
F = 函数依赖集
text

算法输出:

ρ = {R1, R2, ..., Rk}
text

要求:

  1. 每个 Ri 都是 BCNF
  2. 整个分解是无损连接分解

BCNF 的判断标准是:

对于每个非平凡函数依赖 X -> A,
X 必须是超码。
text

如果存在:

X -> A
text

并且:

X 不是超码
text

那么这个关系就不是 BCNF。


这页算法的重点是第 3 步:

如果某个关系 Ri<Ui, Fi> 不是 BCNF,那么一定存在某个违反 BCNF 的函数依赖:

X -> A
text

满足:

X -> A 是非平凡函数依赖
X 不是 Ri 的码
text

然后把 Ri 分解成两个关系:

S1 = X ∪ {A}
S2 = Ui - {A}
text

课件写成:

US1 = XA
US2 = Ui - {A}
text

也就是:

Ri(Ui)  分解为  S1(X,A) 和 S2(Ui - A)
text

注意:X 还保留在 S2 里,因为 US2 = Ui - {A},只删掉右边那个属性 A,不删 X


举个例子:

R(A, B, C)
F = {A -> B}
text

先判断是不是 BCNF。

候选码是:

AC
text

因为:

A -> B
AC -> ABC
text

所以 AC 是码。

但是有依赖:

A -> B
text

其中 A 不是超码,因为:

A+ = {A, B}
text

推不出 C

所以:

R(A,B,C) 不满足 BCNF
text

按照算法,用违反 BCNF 的依赖:

X -> A
text

这里为了避免和属性名混淆,可以写成:

X -> Y
text

具体是:

A -> B
text

则:

S1 = A ∪ B = AB
S2 = U - B = AC
text

所以分解为:

R1(A, B)
R2(A, C)
text

为什么这样分解是无损连接?

因为分解后:

R1(A,B)
R2(A,C)
text

它们的公共属性是:

A
text

而已知:

A -> B
text

也就是说:

公共属性 A -> R1 - R2
text

所以这个二元分解是无损连接的。

通用地看:

S1 = XA
S2 = Ui - {A}
text

两个表的交集是:

S1 ∩ S2 = X
text

因为 X 在两边都有。

而:

X -> A
text

所以:

X -> S1 - S2
text

因此这一步分解一定是无损连接的。

这就是算法为什么能保证“达到 BCNF 的无损连接分解”。


但是要注意一个很重要的问题:

这个算法保证无损连接,但不一定保持函数依赖。

也就是说,拆完以后:

自然连接不会出错
text

但原来的某些函数依赖可能无法只在单张小表里检查,需要连接后才能检查。

所以 BCNF 分解的特点是:

优点:一定无损连接,并且每个子关系达到 BCNF
缺点:不一定保持函数依赖
text

再用一个经典例子说明。

R(A, B, C)
F = {AB -> C, C -> B}
text

先求候选码:

AB 是候选码
AC 也是候选码
text

因为:

AB -> C,所以 AB+ = ABC
C -> B,所以 AC+ = ABC
text

看依赖:

AB -> C
text

AB 是候选码,没问题。

再看:

C -> B
text

C 不是超码,因为:

C+ = {C, B}
text

推不出 A

所以违反 BCNF。

按照算法,用:

C -> B
text

分解:

S1 = C B
S2 = U - B = A C
text

得到:

R1(C, B)
R2(A, C)
text

检查:

R1(C,B)
text

有:

C -> B
text

CR1 的候选码,所以是 BCNF。

R2(A,C)
text

没有非平凡函数依赖,通常也是 BCNF。

所以最终分解为:

{ R1(C,B), R2(A,C) }
text

是 BCNF 无损连接分解。


这页你可以记成一句考试口诀:

发现 X -> A 违反 BCNF,
就拆成 XA 和 U - A。
反复拆,直到每个表都是 BCNF。
text

其中“违反 BCNF”的意思是:

X -> A 非平凡,且 X 不是超码。
text

达到3NF且保持函数依赖的分解算法#

这页是讲:

把关系模式分解到 3NF,并且保持函数依赖。

它和前面的 BCNF 分解算法不同:

BCNF 分解:保证无损连接,但不一定保持函数依赖
3NF 分解:保证保持函数依赖,通常也可通过补候选码保证无损连接
text

先看输入:

R<U, F>
text

其中:

U = 全部属性集
F = 函数依赖集
text

目标是分解成:

ρ = {R1, R2, ..., Rk}
text

并且每个 Ri 都满足 3NF。


第 1 步:求最小函数依赖集 F’

也叫最小覆盖、最小依赖集。

它要满足:

  1. 每个依赖右边只有一个属性:
X -> A
text
  1. 左边没有多余属性。
  2. 整个依赖集中没有多余依赖。

比如:

A -> BC
text

要先拆成:

A -> B
A -> C
text

如果有:

AB -> C
text

但其实:

A -> C
text

也能推出,那么 B 就是多余属性,要删掉。


第 2 步:如果 F’ 中有 X -> A,并且 XA = U,就不用分解

这里:

XA = U
text

意思是:

X ∪ {A} = 全部属性集 U
text

也就是这个函数依赖已经覆盖了整个关系模式。

例如:

R(A,B,C)
F' = {AB -> C}
text

这里:

AB ∪ C = ABC = U
text

所以直接:

ρ = {R}
text

算法结束。

直观理解: 这个依赖已经说明 AB 能决定整个关系,原关系通常已经足够好,不需要再按这个算法拆。


第 3 步:找出没有出现在 F’ 中的属性,单独构成一个关系模式

课件写:

找出不在 F’ 中出现的属性,将它们构成一个关系模式,并从 U 中去掉它们。

这里“不在 F’ 中出现”指的是:

这个属性既没有出现在任何依赖的左边,也没有出现在任何依赖的右边。

比如:

R(A,B,C,D,E)
F' = {A -> B, C -> D}
text

这里 E 没有出现在任何函数依赖里。

所以把它单独拿出来:

R0(E)
text

然后剩余属性继续处理:

U = {A,B,C,D}
text

为什么要这样做? 因为 E 和任何依赖都没关系,放在哪个依赖生成的小表里都不自然,所以先单独保留,避免属性丢失。


第 4 步:对 F’ 中每个 X -> A,构造一个关系模式 XA

也就是:

X -> A
text

生成:

R(X,A)
text

例如:

F' = {A -> B, C -> D}
text

就生成:

R1(A,B)
R2(C,D)
text

如果多个依赖左边相同,可以合并。

比如:

A -> B
A -> C
A -> D
text

不用生成三个表:

AB
AC
AD
text

可以合并成一个:

A B C D
text

也就是:

R(A,B,C,D)
text

因为它们都是由 A 决定的属性。


第 5 步:删除被包含的关系模式

如果出现:

Ui ⊆ Uj
text

就删掉较小的 Ui

例如分解结果里有:

R1(A,B)
R2(A,B,C)
text

因为:

{A,B} ⊆ {A,B,C}
text

所以 R1(A,B) 可以删掉,只保留:

R2(A,B,C)
text

原因是 R1 的属性已经完全包含在 R2 里,单独保留会冗余。


来个完整例子:

R(A,B,C,D)
F = {A -> B, C -> D}
text

最小依赖集就是:

F' = {A -> B, C -> D}
text

按第 4 步:

A -> B 生成 R1(A,B)
C -> D 生成 R2(C,D)
text

所以得到:

ρ = {R1(A,B), R2(C,D)}
text

但是注意:这里属性覆盖不完整吗?

R1 ∪ R2 = {A,B,C,D}
text

完整,所以没丢属性。

每个关系都满足 3NF:

R1(A,B): A -> B,A 是码
R2(C,D): C -> D,C 是码
text

所以结果是 3NF 分解。


不过这里要特别注意一个考试点:

这个算法主要保证“保持函数依赖”。

因为每个依赖 X -> A 都被放进了一个关系模式 XA 里,所以原来的函数依赖可以在分解后的表中直接检查。

比如:

A -> B
text

R1(A,B) 里检查。

C -> D
text

R2(C,D) 里检查。

不需要把表连接回去再检查。


但是如果题目要求:

既保持函数依赖,又无损连接
text

通常还要补一步:

如果分解结果中没有任何一个关系模式包含原关系的候选码,就再加入一个候选码关系。

比如:

R(A,B,C,D)
F = {A -> B, C -> D}
text

候选码是:

AC
text

刚才得到:

R1(A,B)
R2(C,D)
text

没有任何一个表包含候选码 AC

所以为了保证无损连接,通常还要加入:

R3(A,C)
text

最终:

ρ = {R1(A,B), R2(C,D), R3(A,C)}
text

这样才是常见教材里的:

无损连接且保持函数依赖的 3NF 分解
text

所以这页你可以这样记:

3NF 保依赖分解算法:

1. 求最小依赖集 F'
2. 每个 X -> A 生成一个表 XA
3. 相同左部的依赖合并
4. 删除被其他表包含的小表
5. 若要求无损连接,检查是否含候选码;没有就补一个候选码表
text

一句话版:

BCNF 分解是“发现违规就拆”;3NF 分解是“按最小依赖集造表”。

达到3NF既保持函数依赖又无损连接的分解#

这页是在补全前一页的问题:

前一个算法 6.4 可以得到 保持函数依赖的 3NF 分解,但不一定保证 无损连接。 算法 6.5 就是在它的基础上,补一个“候选码关系”,从而保证无损连接。


先看它的输入条件:

设 ρ = {R1<U1,F1>, ..., Rk<Uk,Fk>}
text

这是 R<U,F> 的一个 保持函数依赖的 3NF 分解

也就是说,ρ 已经满足两个性质:

1. 每个 Ri 都是 3NF
2. 保持函数依赖
text

但是它可能还没有保证:

无损连接
text

所以算法 6.5 的目标是:

3NF + 保持函数依赖 + 无损连接
text

关键步骤是:

设 X 为 R<U,F> 的码
text

也就是先找原关系 R 的一个候选码 X

然后检查:

是否存在某个 Ui,使得 X ⊆ Ui
text

意思是:

分解后的某张表里,是否已经包含了原关系的候选码。

如果有,那么:

ρ 即为所求
text

也就是不用再补表。

如果没有,就新增一张表:

R*(X)
text

最终:

τ = ρ ∪ {R*<X, FX>}
text

其中 FXFX 上的投影。

简单说就是:

如果现有分解里没有任何一个关系包含候选码,就额外加一张“候选码表”。


举个例子,还是这个:

R(A,B,C,D)
F = {A -> B, C -> D}
text

先求候选码。

因为:

A -> B
C -> D
text

所以:

AC -> ABCD
text

因此候选码是:

AC
text

用算法 6.4 按最小依赖集造表:

A -> B  得 R1(A,B)
C -> D  得 R2(C,D)
text

所以得到:

ρ = {R1(A,B), R2(C,D)}
text

它保持函数依赖,因为:

A -> B 在 R1 中
C -> D 在 R2 中
text

每张表也都是 3NF。

但是它无损吗?不一定。

检查有没有某个 Ui 包含候选码 AC

U1 = {A,B}
U2 = {C,D}
X = {A,C}
text

显然:

AC ⊄ AB
AC ⊄ CD
text

没有任何一张表包含候选码 AC

所以要补一张:

R3(A,C)
text

最终分解为:

τ = {R1(A,B), R2(C,D), R3(A,C)}
text

这就同时满足:

1. 3NF
2. 保持函数依赖
3. 无损连接
text

为什么补候选码表就能保证无损连接?

直观理解:

候选码能唯一标识原关系中的一行。 如果分解结果中保留了一张候选码表,就相当于保留了原表元组之间的“骨架”,自然连接时不容易乱配出假元组。

比如刚才如果只有:

R1(A,B)
R2(C,D)
text

它们没有公共属性,连接回来会变成笛卡尔积,很容易多出假元组。

加上:

R3(A,C)
text

之后:

R1(A,B) 通过 A 和 R3(A,C) 连接
R2(C,D) 通过 C 和 R3(A,C) 连接
text

R3(A,C)AC 的正确搭配保存下来了,所以不会乱配。


最后这句:

如发现某个 Ui ⊆ X,则应将 Ui 去掉
text

意思是:

如果原来某张表的属性集完全包含在候选码表 X 里面,那它就没必要单独保留了。

比如候选码是:

X = {A,C}
text

如果原来分解里已经有:

R1(A)
text

那:

{A} ⊆ {A,C}
text

加入 R*(A,C) 后,R1(A) 就被包含了,可以删掉。

这是为了去掉冗余关系模式。


所以算法 6.5 可以这样记:

先用算法 6.4 得到保持函数依赖的 3NF 分解;
再看有没有表包含原关系候选码;
有:结束;
没有:补一张候选码表;
如果有小表被候选码表包含,就删掉小表。
text

一句话总结:

算法 6.5 = 算法 6.4 的结果 + 必要时补一个候选码关系,从而保证无损连接。

关于无损分解的总结#

可以把这部分压缩成三个考点

1. 判断是否保持函数依赖
2. 判断是否无损连接
3. 构造“保持FD + 无损连接 + 3NF”的分解
text

这也正好对应 PPT 6.5 的复习点:保持函数依赖如何判定、无损连接如何判定,以及算法 6.5:无损连接 + 保持函数依赖的 3NF 分解。


考点一:保持函数依赖#

它问的是:

分解以后,原来的函数依赖还能不能在各个子关系中检查?
text

设原关系:

R(U, F)
text

分解为:

ρ = {R1(U1), R2(U2), ..., Rn(Un)}
text

每个子关系上能保留一部分函数依赖:

F1 = F 在 U1 上的投影
F2 = F 在 U2 上的投影
...
Fn = F 在 Un 上的投影
text

令:

G = F1 ∪ F2 ∪ ... ∪ Fn
text

如果:

F+ = G+
text

就叫保持函数依赖。PPT 的定义也是:如果 FF1∪F2∪...∪Fn 等价,则该分解保持函数依赖。

考试中通常这样写:

求各子关系上的投影 F1, F2, ...
令 G = F1 ∪ F2 ∪ ...
检查 F 中每条函数依赖是否能由 G 推出。
若都能推出,则保持FD;
若有一条推不出,则不保持FD。
text

例如:

R(A,B,C)
F = {A -> B, B -> C}
ρ = {AB, AC}
text

分解后:

AB 上有 A -> B
AC 上有 A -> C
text

所以:

G = {A -> B, A -> C}
text

但是原来的:

B -> C
text

不能由 G 推出。

所以:

ρ = {AB, AC} 不保持函数依赖
text

记住一句话:

保持FD看的是:约束还能不能在分解后的表中局部检查。
text

考点二:无损连接#

它问的是:

分解后的表自然连接回来,会不会多出假元组?
text

如果:

R = R1 ⋈ R2 ⋈ ... ⋈ Rn
text

就叫无损连接。PPT 中也说,若 RR1、R2、...、Rn 自然连接的结果相等,则该分解具有无损连接性。

最常考的是二分解

R -> R1, R2
text

判断规则:

如果 (R1 ∩ R2) -> (R1 - R2)
或者 (R1 ∩ R2) -> (R2 - R1)
属于 F+
则无损连接。
text

也可以记成:

交集 -> 某一边的差集
text

只要推出其中一个差集就够,不需要两个都推出。

例如:

R(A,B,C)
F = {A -> B, C -> B}
ρ = {AC, BC}
text

计算:

AC ∩ BC = C
AC - BC = A
BC - AC = B
text

因为:

C -> B
text

所以:

C -> BC - AC
text

因此:

ρ = {AC, BC} 是无损连接
text

PPT 例 6.20 正是这样判断的:AC∩BC=CBC-AC=B,由于 C→B∈F+,所以该分解具有无损连接性。

但是注意:

无损连接 ≠ 保持函数依赖
text

无损连接看的是:

连接回来会不会出错
text

保持 FD 看的是:

依赖约束有没有丢
text

这两个是不同性质。


考点三:构造“保持FD + 无损连接 + 3NF”的分解#

这是让你自己分解。

目标是得到:

ρ = {R1, R2, ..., Rn}
text

满足:

1. 每个 Ri 达到 3NF
2. 保持函数依赖
3. 无损连接
text

PPT 算法 6.5 说:先得到一个保持函数依赖的 3NF 分解;若已有某个子模式包含原关系的码,则该分解就是所求;否则再加入一个由码构成的关系。

考试步骤可以写成:

第一步:求最小函数依赖集#

F 化成最小依赖集:

右边单属性;
左边无多余属性;
依赖本身无多余。
text

例如:

A -> BC
text

先拆成:

A -> B
A -> C
text

第二步:按每条 FD 建关系#

对每条:

X -> A
text

建一个关系:

XA
text

如果多条 FD 左部相同,可以合并。

例如:

A -> B
A -> C
text

可以合并成:

ABC
text

而不是非要写成:

AB, AC
text

第三步:检查是否包含候选码#

看当前分解中有没有某个子关系包含原关系 R一个候选码

如果有,不用加。

如果没有,额外加入一个候选码关系。

注意:

只需要加入一个候选码,不需要加入所有候选码。
text

例如候选码有:

AC, BC
text

只需要加:

AC
text

或者只加:

BC
text

任选一个即可。


第四步:删掉被包含的子关系#

如果有:

AB ⊆ ABC
text

那么 AB 可以删掉,保留 ABC


最后压缩成答题口诀#

保持FD:看 F 和各子关系投影并集是否等价。
无损连接:二分解看 交集 -> 某个差集。
3NF分解:最小依赖集建表;若无候选码表,加一个候选码;再删包含项。
text

再短一点就是:

保持FD管“依赖有没有丢”;
无损连接管“连接会不会多假元组”;
3NF分解管“怎么构造一个既保持FD又无损的分解”。
text

需要背诵的一些东西#

数据库系统的特点?#

  • 数据结构化

数据不再只服务某一个程序,而是面向整个组织;不仅记录内部有结构,数据之间也有联系。

  • 数据共享性高、冗余度低、易扩充

  • 数据独立性高

  • 数据由DBMS统一管理和控制

模型#

概念模型#

数据模型#

层次模型#

适合表示一对多关系

不适合多对多关系

网状模型#
关系模型#

三级模式#

概念模式 / 模式

面向数据库设计人员,描述数据整体逻辑结构

外模式

面向应用程序,靠近用户,描述视图。

不同用户的需求创建不同视图

内模式

面向物理层,描述数据采用什么样的数据结构存储和获取

二级映像#

外模式 / 概念模式映像

保持逻辑独立性:修改概念模式基本表时,不影响外模式

  • 每一个外模式都对应一个映像
  • 映像定义包含在外模式描述中

概念模式 / 内模式映像

保持物理独立性:修改了内模式,不影响上层的概念模式和外模式

  • 唯一
  • 包含在模式描述中

三个完整性#

实体完整性#

所有主属性都不能为控制

参照完整性#

外码必须出现在引用中,或者为空值

用户定义完整性#

下次一定好好复习(

《数据库系统原理》期末复习
https://lan-fa.github.io/blog/database-system-notes
Author LanFa
Published at June 25, 2026
Comment seems to stuck. Try to refresh?✨