基本概念回顾
基本概念解释
-
1、非主属性:在一个关系表中,所有不是主键的属性都称为非主属性。这些属性用于描述主键所代表的实体的特征或性质。
-
2、主键:主键是一个或多个属性的组合,通过它可以唯一标识表中的每一条记录。
-
3、完全依赖:一个非主属性完全依赖于主键,意味着这个非主属性的值可以通过主键的单值唯一确定,而不是通过主键的部分值来确定。
-
4、部分依赖:如果一个非主属性只依赖于主键的一部分(而不是整个主键),那么就称之为部分依赖。
第二范式
第二范式的要求
第二范式要求所有非主属性必须完全依赖于主键的整个值,而不能只依赖于主键的一部分。
示例
学生选课表
假设有一个表 StudentCourses
,结构如下:
学生ID | 课程ID | 学生姓名 | 课程名称 |
---|---|---|---|
1 | 101 | 张三 | 数学 |
1 | 102 | 张三 | 英语 |
2 | 101 | 李四 | 数学 |
2 | 103 | 李四 | 物理 |
在这个表中:
- 主键是
(学生ID, 课程ID)
,因为一名学生可以选修多门课程。 - 非主属性包括 学生姓名 和 课程名称。
分析依赖
-
学生姓名完全依赖于学生ID,而不依赖课程ID。换句话说,如果我知道某个学生的ID为1,我就可以确定他的姓名是“张三”。这表明
学生姓名
对于学生ID
存在部分依赖(只依赖于主键的一部分),而且不同课程下对应同一个学生的姓名重复。 -
课程名称完全依赖于课程ID,而不依赖学生ID。例如,不论哪个学生上101课程,该课程的名称都是“数学”。
因此,在这个表中,存在部分依赖的问题,即 学生姓名
仅依赖于 学生ID
,而 课程名称
仅依赖于 课程ID
。
遵循第二范式的正确结构
为了将这个表转换为符合第二范式,我们可以将数据分成两个表:
学生表(Students)
学生ID | 学生姓名 |
---|---|
1 | 张三 |
2 | 李四 |
课程表(Courses)
课程ID | 课程名称 |
---|---|
101 | 数学 |
102 | 英语 |
103 | 物理 |
选课表(StudentCourses)
学生ID | 课程ID |
---|---|
1 | 101 |
1 | 102 |
2 | 101 |
2 | 103 |
现在,每个非主属性都完全依赖于其新的主键:
- 在
Students
表中,学生姓名
完全依赖于学生ID
。 - 在
Courses
表中,课程名称
完全依赖于课程ID
。 - 在
StudentCourses
表中,所有非主属性(也就是在此表并不存在)完全依赖于复合主键(学生ID, 课程ID)
。
总结
- 完全依赖:非主属性必须依赖于主键的整体,而不能仅依赖于主键的一部分。
- 避免冗余:第二范式的设计目的是消除冗余数据,确保每个非主属性的值都可以通过主键的整个值唯一确定。
第三范式
第三范式的引入
第三范式的引入是为了解决在第二范式的基础上可能依然存在的数据冗余和数据更新问题。尽管第二范式确保了非主属性完全依赖于主键,但在某些情况下,非主属性之间可能还存在依赖关系,这种依赖关系如果处理不当,也会导致数据冗余和数据不一致的问题。
第三范式的定义
第三范式的定义如下:
- 一个关系模式符合第三范式的条件是其必须符合第二范式,并且所有非主属性必须直接依赖于主键,而不能存在非主属性之间的传递依赖。
什么是传递依赖?
传递依赖是指如果一个非主属性依赖于另一个非主属性,而那个非主属性又依赖于主键,那么就存在传递依赖。换句话说,非主属性不能通过另一个非主属性间接获得其值。
通过示例理解第三范式
让我们通过一个具体的示例来理解第三范式的应用。
示例表:学生详细信息
假设我们现在有一个表 StudentDetails
,结构如下:
学生ID | 学生姓名 | 系ID | 系名称 | 系主任 |
---|---|---|---|---|
1 | 张三 | 101 | 计算机 | 王教授 |
2 | 李四 | 101 | 计算机 | 王教授 |
3 | 王五 | 102 | 数学 | 李教授 |
在这个表中:
- 主键是
学生ID
。 - 非主属性包括
学生姓名
、系ID
、系名称
和系主任
。
分析依赖
学生姓名
完全依赖于学生ID
。系ID
完全依赖于学生ID
(因为每个学生属于一个特定的系)。系名称
完全依赖于系ID
。系主任
完全依赖于系ID
。
这里存在传递依赖:系名称
和 系主任
都依赖于 系ID
,而 系ID
又依赖于 学生ID
。这意味着同一个系的信息(系名称和系主任)会重复出现在多个学生记录中,导致数据冗余。
遵循第三范式的正确结构
为了将这个表转换为符合第三范式,我们可以将数据分成两个表:
学生表(Students)
学生ID | 学生姓名 | 系ID |
---|---|---|
1 | 张三 | 101 |
2 | 李四 | 101 |
3 | 王五 | 102 |
系表(Departments)
系ID | 系名称 | 系主任 |
---|---|---|
101 | 计算机 | 王教授 |
102 | 数学 | 李教授 |
现在,Students
表中的所有非主属性(学生姓名
和 系ID
)都直接依赖于主键 学生ID
,而不再存在传递依赖。在 Departments
表中,系名称
和 系主任
都直接依赖于 系ID
,也不再存在传递依赖。
总结
- 第三范式的目标:消除非主属性之间的传递依赖,确保每个属性直接依赖于主键。
- 传递依赖的消除:通过对数据进行拆分,确保每个属性(非主属性)只依赖于主键,而不是依赖于其他非主属性。
- 优点:第三范式能够进一步减少数据冗余,提升数据的一致性,并简化数据更新和维护。
BCNF修正的第三范式
基础概念复习
- 函数依赖:如果在一个关系中,属性集
X
的值可以唯一确定属性集Y
的值,那么我们说Y
函数依赖于X
,记作X → Y
。 - 候选键:一个或多个属性的组合,通过它可以唯一标识表中的每一条记录,且没有任何冗余部分。
- 主属性:包含在某个候选键中的属性。
- 非主属性:不包含在任何候选键中的属性。
第三范式(3NF)回顾
第三范式要求:
- 第二范式:所有非主属性必须完全依赖于主键。
- 无传递依赖:所有非主属性之间不能存在传递依赖。
BCNF 的定义
BCNF 在第三范式的基础上增加了更严格的要求:
- 非平凡函数依赖:如果
X → Y
是一个非平凡的函数依赖(即Y
不是X
的子集),那么X
必须是候选键。
换句话说,对于每一个非平凡的函数依赖,决定因素 X
必须是一个候选键。这个要求确保了数据没有任何多余的依赖关系。
示例分析
让我们通过一个具体的例子来理解 BCNF。
示例表:供应商零件表
假设有一个表 SupplierParts
,结构如下:
供应商ID | 零件ID | 供应商城市 |
---|---|---|
S1 | P1 | 北京 |
S2 | P2 | 上海 |
S3 | P3 | 北京 |
在这个表中:
- 主键是
(供应商ID, 零件ID)
。
分析函数依赖
供应商ID → 供应商城市
:每个供应商(供应商ID
)都有其对应的城市(供应商城市
)。零件ID
不决定任何其他属性。
存在的问题
在这个表中,供应商城市
依赖于 供应商ID
,但 供应商ID
不是主键。这导致了函数依赖 供应商ID → 供应商城市
,且 供应商ID
是一个非主属性。
不符合 BCNF
根据 BCNF 的要求,每个非平凡的函数依赖的决定因素必须是候选键。在这个例子中,存在函数依赖 供应商ID → 供应商城市
,但 供应商ID
不是候选键,因此这个表不符合 BCNF。
拆分表以符合 BCNF
为了将这个表转换为符合 BCNF,我们可以将数据分成两个表:
供应商表(Suppliers)
供应商ID | 供应商城市 |
---|---|
S1 | 北京 |
S2 | 上海 |
S3 | 北京 |
供应商零件表(SupplierParts)
供应商ID | 零件ID |
---|---|
S1 | P1 |
S2 | P2 |
S3 | P3 |
现在:
- 在
Suppliers
表中,供应商ID
是主键,且符合 BCNF 的要求。 - 在
SupplierParts
表中,供应商ID
和零件ID
组合成主键,且符合 BCNF 的要求。
总结
- BCNF 的要求:对于每一个非平凡的函数依赖
X → Y
,X
必须是候选键。 - 解决问题:通过分解表,确保每个表中的决定因素都是候选键。
- 优点:BCNF 进一步减少了数据冗余,确保数据的结构化和一致性。