各种函数依赖及规范化解决
假设我们有一个表格存储学生成绩信息:
学生成绩表
学号 (SNo) | 课程号 (CNo) | 分数 (Score) | 姓名 (Name) | 班级 (Class) |
---|
1. 函数依赖分类
前提:X——>Y(即X决定Y)
1.1 分类1:平凡/非平凡依赖
依据:依赖关系中Y是否为X的子集。即基于属性集的包含关系(Y是否属于X)
- 平凡函数依赖:若Y⊆X,则X→Y为平凡函数依赖。例如关系模式(学号,姓名,年级)中,(学号,姓名)→姓名属于平凡依赖,因“姓名”是属性组(学号,姓名)的子集。
- 非平凡函数依赖:若X→Y且Y∉X,则为非平凡依赖。例如学号→年级,因“年级”不包含于“学号”这一属性。
1. 平凡依赖(Trivial Dependency)
平凡依赖指的是属性集中的某个属性依赖于自身。不需要从其他属性获得信息。
例子:
- 在表中,对于任何属性集 {SNo, CNo, Score},平凡依赖有 {SNo} → SNo, {CNo, Score} → Score 等。
2. 非平凡依赖(Non-Trivial Dependency)
非平凡依赖是指如果 X → Y 成立并且 Y 不属于 X,这称为非平凡依赖。
例子:
- 在学生成绩表中,{SNo, CNo} → Score 是一个非平凡依赖,因为分数 Score 不属于组合键 {SNo, CNo}。
1.2 分类2:完全/部分(传递)依赖
依据:依赖关系中X的真子集是否决定Y,或是否通过中间属性间接决定。 即 基于依赖的完整性(是否需全部属性)或传递性(是否通过中间属性)
- 完全函数依赖:Y完全依赖于X,当且仅当X的任意真子集均无法决定Y。例如(学号,课程号)→成绩,因单独学号或课程号均无法确定成绩。
- 部分函数依赖:Y部分依赖于X,若存在X的真子集X'能决定Y。例如(学号,课程号)→姓名,因仅“学号”即可决定“姓名”,无需组合属性。
- 传递函数依赖:通过中间属性间接依赖,即X→Y,Y→Z,但Y↛X,则X→Z为传递依赖。例如学号→系名,系名→系主任,则学号→系主任为传递依赖。
1. 完全函数依赖(Full Functional Dependency)
完全函数依赖是指 Y 对 X 是完全依赖的,但对 X 的任何真子集都不依赖,即去掉任何一个 X 的成分,依赖关系不再成立。
例子:
- {SNo, CNo} → Score 是完全函数依赖,因为只有通过学号和课程号的组合才能唯一确定一个分数。
2. 部分函数依赖(Partial Functional Dependency)
部分函数依赖指的是在一个复合主键中,如果有一个非主属性依赖于主键的一部分,而不是整个主键。
假设学生成绩表的主键是由“学号 (SNo)”和“课程号 (CNo)”组合而成,这样能唯一标识一条记录。
然而,“姓名 (Name)”和“班级 (Class)”只依赖于“学号 (SNo)”而非整个主键,因为一个学生会有多个课程。因此,这里有部分函数依赖:
- 名称 (Name) 依赖于 学号 (SNo)
- 班级 (Class) 依赖于 学号 (SNo)
3. 传递函数依赖(Transitive Dependency)
传递函数依赖是指如果存在 X → Y 和 Y → Z,那么就有 X → Z。通常在属性集合中隐含信息传递。
例子:
- 假设我们有一个额外的表记录班级信息,比如班级代表:
班级信息表
| 班级 (Class) | 班长 (Leader) |
|--------------|---------------|
在这种情况下,学生成绩表中的班级对班长存在传递函数依赖:{SNo} → {Class} 和 {Class} → {Leader},因此 {SNo} → {Leader} 是传递依赖。
这些不同类型的依赖关系帮助我们识别数据库设计中的冗余、异常,并指导我们进行范式化处理。
2. 规范化
2.1 不规范
不会发生插入异常、删除异常、更新异常,数据冗余应尽可能少。
https://fangkaipeng.com/?p=921
例:关系模式R中 (学生的学号(Sno)、所在系(Sdept)系主任姓名(Mname)、课程名(Cname)、成绩(Grade))
- 数据冗余
比如,每一个系的系主任姓名重复出现,重复次数与该系所有学生的所有课程成绩出现次数相同,如表6.1所示。这将浪费大量的存储空间。- 冗余的根本原因是存在部分函数依赖(非完全函数依赖):
- 候选键为(Sno, Cname),但非主属性 Sdept 和 Mname 仅依赖于 Sno(候选键的真子集),而非完整的候选键23。
- 即:Sno → Sdept → Mname,而(Sno, Cname)→ Sdept 是冗余的依赖路径
- 冗余的根本原因是存在部分函数依赖(非完全函数依赖):
- 更新异常(update anomalies)
由于数据冗余,当更新数据库中的数据时,系统要付出很大的代价来维护数据库的完整性,否则会面临数据不一致的危险。比如,某系更换系主任后,必须修改与该系学生有关的每一个元组。 - 插入异常(insertion anomalies)
如果一个系刚成立,尚无学生,则无法把这个系及其系主任的信息存入数据库 - 删除异常(deletion anomalies)
如果某个系的学生全部毕业了,则在删除该系学生信息的同时,这个系及其系主任的信息也丢掉了。
上述的关系模式不是一个好的关系模式。这是由存在于模式中的某些数据依赖引起的,可以通过分解关系模式来消除其中不合适的数据依赖。
2.2 解决:规范化
1NF:
第一:属性 不可再分
(消除多值属性与重复字段)
2NF:
2NF:满足1NF,且 非主属性 完全依赖 主属性,而不是部分依赖(消除冗余更新异常)
将原关系 R 分解为以下两个表:
-
学生-系表(Student_Dept):
-
属性:Sno(学号)、Sdept(系)、Mname(系主任)
-
候选键:Sno(完全函数依赖)
-
依赖关系:Sno → Sdept → Mname。
-
- 选课成绩表(SC):
- 属性:Sno(学号)、Cname(课程名)、Grade(成绩)
- 候选键:(Sno, Cname)(完全函数依赖)
- 依赖关系:(Sno, Cname)→ Grade。
通过分解,原关系中的冗余数据和部分依赖被消除,满足第二范式要求
3NF
第三:满足第二,且消除传递依赖
(消除传递依赖导致的数据冗余)
BCNF
BCNF:满足3NF,且对于每一个非平凡的函数依赖 X -> Y
,X
必须是一个候选键,而不仅仅涉及主键(或候选键)的一部分。
示例1:
假设我们有一个表 Enrollments
:候选键(CourseID, ProfessorID),但是有依赖 CourseID -> ProfessorID,违反BCNF
CourseID | ProfessorID | StudentID | ProfessorName |
---|---|---|---|
C001 | P001 | S001 | Dr. Smith |
C002 | P002 | S002 | Dr. Johnson |
假设 CourseID
是唯一的课程标识,所以 CourseID -> ProfessorID
是一个函数依赖。在这种情况下,ProfessorName
对 ProfessorID
和 CourseID
的依赖不是符合BCNF的,因为 ProfessorID
本身应该是一个候选键。
为使表进入BCNF,我们需要分解表:
ProfessorDetails
表:
ProfessorID | ProfessorName |
---|---|
P001 | Dr. Smith |
P002 | Dr. Johnson |
Enrollments
表:
CourseID | ProfessorID | StudentID |
---|---|---|
C001 | P001 | S001 |
C002 | P002 | S002 |
在这里,通过分解 we 能够消除 ProfessorName
的传递依赖,因为它直接从 ProfessorID
派生,而 ProfessorID
是一个候选键。
通过这些示例,我们可以看到,2NF 主要关注消除部分依赖,而 BCNF 关注任何情况下的函数依赖,确保候选键的充分利用。
示例2:
好的,让我们来看看一个符合第二范式 (2NF) 但是不符合 Boyce-Codd 范式 (BCNF) 的例子。
示例表:Assignments
假设我们有一个表 Assignments
,其中:
- 主键是组合键
(StudentID, AssignmentID)
。 - 这些属性意味着每个学生可以有多个作业,每个作业可以由不同的课程指定。
假设表结构如下:
StudentID | AssignmentID | CourseID | CourseInstructor |
---|---|---|---|
S001 | A001 | C001 | Dr. Smith |
S002 | A002 | C002 | Dr. Johnson |
S001 | A003 | C001 | Dr. Smith |
S002 | A004 | C003 | Dr. Williams |
依赖关系:
- 主键为
(StudentID, AssignmentID)
。 CourseID
和CourseInstructor
是对(StudentID, AssignmentID)
的完全依赖,因为它们由两个属性决定。CourseID -> CourseInstructor
是一个非主属性到非主属性的依赖。
2NF 分析
该表已满足 1NF,因为所有属性均为原子值。
通过查看 (StudentID, AssignmentID)
的组合,我们看不到部分依赖。
因此,该表符合 2NF,因为所有非主属性都对整个主键而不是部分主键完全依赖。
BCNF 分析
BCNF 要求每个函数依赖具有候选键。
CourseID -> CourseInstructor
是一个非平凡的函数依赖,但CourseID
不是候选键。
这个非平凡的依赖性违反了 BCNF 的要求,因为CourseID
不是候选键。
分解到BCNF
为了使表达到 BCNF,我们可以通过将表分解为两个表:
Courses
表:
CourseID | CourseInstructor |
---|---|
C001 | Dr. Smith |
C002 | Dr. Johnson |
C003 | Dr. Williams |
Assignments
表:
StudentID | AssignmentID | CourseID |
---|---|---|
S001 | A001 | C001 |
S002 | A002 | C002 |
S001 | A003 | C001 |
S002 | A004 | C003 |
这样,CourseInstructor
直接依赖于候选键 CourseID
,符合 BCNF 要求。在这种情况下,我们消除了非主键间的依赖关系,并确保所有非平凡的函数依赖都有一个候选键。