软考中级软件设计师高频考点:候选码闭包求解与范式判定及无损分解真题详解(附历年真题·百度网盘)
🎯 所属考纲模块:专题三 数据库系统与规范化理论(考点 03:规范化理论)
🏷️ 考查题型:上午综合知识单选题(必考 2~3 题)+ 下午试题二数据库设计(必考 15 分)
📌 核心知识点:函数依赖、属性闭包计算、四分类快速求候选码、1NF/2NF/3NF/BCNF 范式层级判定、无损连接性与保持函数依赖分解
💻 实操验证环境:Python 3.10+ / Linux x86_64(文内附属性闭包与候选码全自动枚举求解验证代码)
在关系数据库设计中,一个设计不当的关系模式往往会导致严重的数据冗余、更新异常、插入异常和删除异常。为了消除这些异常,规范化理论通过函数依赖(Functional Dependency)来指导模式分解,使关系逐步从第一范式向更高范式演进。
在软考软件设计师考试中,规范化理论是贯穿**上午客观题(3分左右)与下午第二道大题(15分)**的绝对核心得分点。其中,“四分类法极速求候选码”、“各范式层级违规依赖特征定位”以及“无损连接判定公式”是考生的必修课。本文将用最精炼的逻辑框架带你彻底攻克这一硬核堡垒。
一、 函数依赖与四分类法极速求解候选码
设 R ( U ) R(U) R(U) 是属性集 U U U 上的关系模式, X X X 和 Y Y Y 是 U U U 的子集。若对于 R ( U ) R(U) R(U) 的任意一个可能的关系 r r r, r r r 中不可能存在两个元组在 X X X 上的属性值相等而在 Y Y Y 上的属性值不等,则称 X X X 函数决定 Y Y Y,或称 Y Y Y 函数依赖于 X X X,记作 X → Y X \to Y X→Y。
1. 属性闭包(Attribute Closure, X + X^+ X+)算法
在函数依赖集 F F F 下,能够由属性集 X X X 依据阿姆斯特朗公理系统推导出的所有属性的集合,称为 X X X 关于 F F F 的属性闭包,记作 X F + X_F^+ XF+。
- 闭包求解三步走:
- 初始化闭包: X ( 0 ) = X X^{(0)} = X X(0)=X;
- 扫描函数依赖集 F F F 中所有 A → B A \to B A→B:若 A ⊆ X ( i ) A \subseteq X^{(i)} A⊆X(i),则将 B B B 并入集合,得到 X ( i + 1 ) = X ( i ) ∪ B X^{(i+1)} = X^{(i)} \cup B X(i+1)=X(i)∪B;
- 重复执行第 2 步,直到集合不再扩大。最终得到的集合即为 X + X^+ X+。
2. 四分类法极速秒杀候选码(黄金秘籍)
对于给定的关系模式 R ( U , F ) R(U, F) R(U,F),将所有属性划分为 4 类:
- L 类(Left Only):仅在函数依赖的左边出现;
- R 类(Right Only):仅在函数依赖的右边出现;
- N 类(Neither):在函数依赖的两边均未出现(孤立属性);
- LR 类(Left & Right):在函数依赖的两边均有出现。
属性全集 U 划分
┌─────────────┬─────────────┬─────────────┬─────────────┐
│ L 类 │ N 类 │ LR 类 │ R 类 │
│ (仅在左侧) │ (两边未现) │ (两边均现) │ (仅在右侧) │
└─────────────┴─────────────┴─────────────┴─────────────┘
│ │ │ │
▼ ▼ │ ▼
【必定包含在码中】 │ 【绝不可能包含在码中】
│ │ │
└──────┬──────┘ │
▼ ▼
计算 (L ∪ N)+ ───[满?]──>【是】──> 候选码唯一且就是 L ∪ N
│
【否】
│
▼
依次与 LR 类属性组合计算闭包,直到等于 U
- 秒杀推论:
- 推论 1:L 类和 N 类属性必定包含在候选码中;
- 推论 2:R 类属性绝对不可能包含在任何候选码中(直接排除!);
- 推论 3:先求 ( L ∪ N ) + (L \cup N)^+ (L∪N)+,若其闭包已等于属性全集 U U U,则 L ∪ N L \cup N L∪N 就是唯一的候选码!
- 推论 4:若 ( L ∪ N ) + ≠ U (L \cup N)^+ \ne U (L∪N)+=U,则只需将 L ∪ N L \cup N L∪N 分别与 LR 类中的属性进行组合,测试其闭包是否覆盖 U U U。
二、 四大范式判定标准与演进层次
范式等级决定了关系的规范化程度: 1 NF ⊂ 2 NF ⊂ 3 NF ⊂ BCNF 1\text{NF} \subset 2\text{NF} \subset 3\text{NF} \subset \text{BCNF} 1NF⊂2NF⊂3NF⊂BCNF。
1. 概念术语界定
- 候选码(Candidate Key):能唯一标识元组且不含多余属性的最小属性组。
- 主属性(Prime Attribute):包含在任何一个候选码中的属性。
- 非主属性(Non-prime Attribute):不包含在任何候选码中的属性。
2. 范式逐级判定口诀
① 第一范式(1NF)
- 准则:每个属性都是不可分割的原子数据项。
- 违规表现:表中套表,某个字段包含逗号隔开的多个值。
② 第二范式(2NF)
- 准则:已是 1NF,且每一个非主属性完全依赖于码(即消除了非主属性对码的部分函数依赖)。
- 判定秒招:
- 若候选码是单属性,则该关系模式必定自动满足 2NF(因为不可能存在真子集)!
- 若候选码是复合属性(如 ( A , B ) (A, B) (A,B)),且存在 A → C A \to C A→C( C C C 为非主属性),则违背 2NF。
③ 第三范式(3NF)
- 准则:已是 2NF,且消除了非主属性对码的传递函数依赖(即任何非主属性都不能通过其他非主属性间接决定)。
- 判定秒招:
- 寻找是否存在形如 X → Y → Z X \to Y \to Z X→Y→Z 的依赖链,其中 X X X 是候选码,但 Y Y Y 是非主属性, Z Z Z 也是非主属性。若存在则不是 3NF。
④ BCNF(Boyce-Codd 范式)
- 准则:消除了所有属性(包含主属性与非主属性)对码的部分依赖与传递依赖。
- 通俗定义:在关系模式的所有函数依赖 X → Y X \to Y X→Y 中, X X X 都必须包含候选码(即 X X X 是超码)!
三、 模式分解的两大特性判定准则
将一个不满足高级范式的关系模式分解为多个子模式时,必须遵守以下两项质量标准:
- 无损连接性(Lossless Join):将分解后的子关系进行自然连接时,必须能够完全无损地还原原始关系,不能产生虚假的伪元组(幽灵元组)。
- 两子模式分解定理(必背公式):
若模式 R R R 分解为两个子模式 ρ = { R 1 , R 2 } \rho = \{R_1, R_2\} ρ={R1,R2},且 F F F 为 R R R 上的函数依赖集,则该分解具有无损连接性的充要条件是:
( R 1 ∩ R 2 ) → ( R 1 − R 2 ) ∈ F + 或 ( R 1 ∩ R 2 ) → ( R 2 − R 1 ) ∈ F + (R_1 \cap R_2) \to (R_1 - R_2) \in F^+ \quad \text{或} \quad (R_1 \cap R_2) \to (R_2 - R_1) \in F^+ (R1∩R2)→(R1−R2)∈F+或(R1∩R2)→(R2−R1)∈F+
口诀:两子模式的公共属性必须是其中至少一个子模式的超码!
- 两子模式分解定理(必背公式):
- 保持函数依赖(Dependency Preserving):原关系模式中所有的函数依赖,在分解后的各个子模式中都能直接成立或通过投影逻辑导出。
四、 Python 验证实验:属性闭包与候选码全自动求解器
我们编写一段通用算法,输入属性集与依赖集,自动分类并求解所有候选码:
from itertools import combinations
def compute_closure(attrs, fds):
closure = set(attrs)
changed = True
while changed:
changed = False
for lhs, rhs in fds:
if set(lhs).issubset(closure):
for r in rhs:
if r not in closure:
closure.add(r)
changed = True
return closure
def find_candidate_keys(u_attrs, fds):
u = set(u_attrs)
left_attrs = set()
right_attrs = set()
for lhs, rhs in fds:
left_attrs.update(lhs)
right_attrs.update(rhs)
l_class = left_attrs - right_attrs
r_class = right_attrs - left_attrs
n_class = u - (left_attrs | right_attrs)
lr_class = left_attrs & right_attrs
print(f"属性全集 U: {u}")
print(f"L类(必选): {l_class}, R类(排除): {r_class}, N类(必选): {n_class}, LR类(候选): {lr_class}")
core = l_class | n_class
if compute_closure(core, fds) == u:
return [sorted(list(core))]
candidate_keys = []
found_size = None
for k in range(1, len(lr_class) + 1):
for comb in combinations(sorted(lr_class), k):
candidate = core | set(comb)
if compute_closure(candidate, fds) == u:
candidate_keys.append(sorted(list(candidate)))
if candidate_keys:
break
return candidate_keys
if __name__ == "__main__":
U = ['A', 'B', 'C', 'D', 'E']
F = [
(['A'], ['B']),
(['B', 'C'], ['D']),
(['E'], ['C'])
]
print("=== 规范化理论:四分类法求候选码 ===")
keys = find_candidate_keys(U, F)
print(f"--> 计算得到的候选码为: {keys}\n")
控制台实测输出:
=== 规范化理论:四分类法求候选码 ===
属性全集 U: {'A', 'B', 'C', 'D', 'E'}
L类(必选): {'A', 'E'}, R类(排除): {'D'}, N类(必选): set(), LR类(候选): {'B', 'C'}
--> 计算得到的候选码为: [['A', 'E']]
五、 历年机考真题切片与深度解析
💡 真题切片一(四分类求候选码真题)
【官方真题】 给定关系模式 R ( A , B , C , D , E ) R(A, B, C, D, E) R(A,B,C,D,E),其函数依赖集 F = { A → B , B C → D , E → C } F = \{ A \to B, BC \to D, E \to C \} F={A→B,BC→D,E→C}。则关系模式 R R R 的候选码为( )。
A. A A A
B. A E AE AE
C. A B C ABC ABC
D. A D E ADE ADE
- 解题推导:
- 列出左侧属性集合: L all = { A , B , C , E } L_{\text{all}} = \{A, B, C, E\} Lall={A,B,C,E};
- 列出右侧属性集合: R all = { B , D , C } R_{\text{all}} = \{B, D, C\} Rall={B,D,C};
- 四分类划分:
- L 类(只在左边): { A , B , C , E } − { B , D , C } = { A , E } \{A, B, C, E\} - \{B, D, C\} = \{A, E\} {A,B,C,E}−{B,D,C}={A,E};
- R 类(只在右边): { B , D , C } − { A , B , C , E } = { D } \{B, D, C\} - \{A, B, C, E\} = \{D\} {B,D,C}−{A,B,C,E}={D};
- N 类(两边均无): ∅ \emptyset ∅;
- LR 类(两边均有): { B , C } \{B, C\} {B,C}。
- 根据推论,
A
A
A 和
E
E
E 必须包含在任何候选码中。我们计算核心集合
{
A
,
E
}
\{A, E\}
{A,E} 的属性闭包:
- { A , E } ( 0 ) = { A , E } \{A, E\}^{(0)} = \{A, E\} {A,E}(0)={A,E};
- 根据 A → B A \to B A→B,并入 B ⟹ { A , B , E } B \implies \{A, B, E\} B⟹{A,B,E};
- 根据 E → C E \to C E→C,并入 C ⟹ { A , B , C , E } C \implies \{A, B, C, E\} C⟹{A,B,C,E};
- 根据 B C → D BC \to D BC→D,并入 D ⟹ { A , B , C , D , E } = U D \implies \{A, B, C, D, E\} = U D⟹{A,B,C,D,E}=U。
- { A , E } + = U \{A, E\}^+ = U {A,E}+=U,因此 A E AE AE 是唯一的候选码!
- 正确答案:B
💡 真题切片二(范式层级判定与消除部分依赖)
【官方真题】 给定关系模式 R ( S n o , S n a m e , C n o , C n a m e , G r a d e ) R(Sno, Sname, Cno, Cname, Grade) R(Sno,Sname,Cno,Cname,Grade),函数依赖集 F = { S n o → S n a m e , C n o → C n a m e , ( S n o , C n o ) → G r a d e } F = \{ Sno \to Sname, Cno \to Cname, (Sno, Cno) \to Grade \} F={Sno→Sname,Cno→Cname,(Sno,Cno)→Grade}。该关系模式最高属于( ),若要将其规范化为 2NF,应分解为( )。
A. 1NF
B. 2NF
C. 3NF
D. BCNF
- 解题推导:
- 求解候选码:只有 ( S n o , C n o ) (Sno, Cno) (Sno,Cno) 能唯一决定所有属性,因此候选码为 ( S n o , C n o ) (Sno, Cno) (Sno,Cno)。
- 确定主属性与非主属性:
- 主属性: S n o , C n o Sno, Cno Sno,Cno;
- 非主属性: S n a m e , C n a m e , G r a d e Sname, Cname, Grade Sname,Cname,Grade。
- 检验非主属性的函数依赖:
- S n o → S n a m e Sno \to Sname Sno→Sname:非主属性 S n a m e Sname Sname 仅依赖于主键的一部分 S n o Sno Sno(发生部分函数依赖);
- C n o → C n a m e Cno \to Cname Cno→Cname:非主属性 C n a m e Cname Cname 仅依赖于主键的一部分 C n o Cno Cno(发生部分函数依赖)。
- 存在非主属性对码的部分函数依赖,因此该关系模式不满足 2NF,最高只能属于 1NF。
- 规范化为 2NF 必须消除部分依赖,分解为:
- R 1 ( S n o , S n a m e ) R_1(Sno, Sname) R1(Sno,Sname)
- R 2 ( C n o , C n a m e ) R_2(Cno, Cname) R2(Cno,Cname)
- R 3 ( S n o , C n o , G r a d e ) R_3(Sno, Cno, Grade) R3(Sno,Cno,Grade)
- 正确答案:A
附:相关真题与复习资料
本文涉及的规范化理论与候选码范式分解专项历年真题解析已归档,需要可自取:
百度网盘: https://pan.baidu.com/s/1SwVEmITKHveCupjP_VH7pA 提取码: rk02
备用通道: https://pan.quark.cn/s/bd0eb7dd5673
本文聚焦软考中级软件设计师数据库规范化理论核心考点,系统讲解属性闭包计算、四分类法求候选码、1NF/2NF/3NF/BCNF逐级判定标准、无损连接性与函数依赖保持的分解判定方法,并辅以Python自动化验证和历年真题深度解析,覆盖上午单选与下午设计大题高频得分点。
&spm=1001.2101.3001.5002&articleId=169065751&d=1&t=3&u=ae39adfea26b4c59801c414ac0defc5b)
343

被折叠的 条评论
为什么被折叠?



