依赖与范式
一、函数依赖
1.什么是函数依赖
函数依赖描述了一个属性集合与另一个属性集合之间的约束关系。设关系模式R,属性集X、Y是R的子集,如果对于R的任意一个合法实例,当两个元组的X值相等时,它们的Y值也必然相等,则称 X函数决定Y ,或 Y函数依赖于X ,记作 X → Y。
生活实例 :
-
身份证号 → 姓名(一个身份证号只对应一个人)
-
学号 → 姓名、出生日期(学号唯一确定学生信息)
-
(学号, 课程号) → 成绩(两者共同决定分数)
反例 :姓名 → 年龄(不成立,因为可能有重名且年龄不同)
2.函数依赖的分类
-
平凡函数依赖 :如果 Y ⊆ X,则 X → Y 是平凡的。例如 (学号, 姓名) → 学号。这种依赖总是成立,没有实际约束意义。
-
非平凡函数依赖 :Y ⊈ X,例如 学号 → 姓名。
-
完全函数依赖 :在 X → Y 中,如果去掉 X 中的任何一个属性,依赖就不再成立,则称 Y 完全函数依赖于 X。
- 例子 :在 (学号, 课程号) → 成绩 中,单独的学号或单独的课程号都无法决定成绩,因此成绩完全函数依赖于复合主键。
-
部分函数依赖 :如果 Y 只依赖于 X 的一个真子集。
- 例子 :在 (学号, 课程号) → 姓名 中,只用学号就可以决定姓名,所以姓名部分依赖于复合主键。
-
传递函数依赖 :若 X → Y,Y → Z,且 Y ⊈ X 且 Z ⊈ Y,且 Y 不决定 X,则称 Z 传递依赖于 X。
- 例子 :学号 → 系名,系名 → 系主任,因此学号传递决定系主任。
重点关注完全函数依赖、部分函数依赖和传递函数依赖
二、范式
1.第一范式(1NF)
定义 :关系模式R的所有属性值都是不可再分的原子值。
反例1(多值字段) :
一个“学生信息”表中有一列“联系方式”,存储类似 “电话:123456, 邮箱:abc@xx.com” 的字符串,这违反了1NF,因为该列包含了两个不同类别的信息。
反例2(重复组) :
一个学生表数据中“所选课程”一栏出现“C001, C002......”多条数据,导致“所选课程”列中存了多个课程,不是原子值,违反1NF。
解决办法 :
-
拆分列:将“联系方式”拆成“电话”和“邮箱”两列。
-
拆分行:将“所选课程”扩展为多行,一行一个课程。
小结 :第一范式是关系数据库的基本要求 ,目前大多数数据库在设计表时自然会满足。
2.第二范式(2NF)
定义 :在满足1NF的基础上,消除非主属性对候选键的部分函数依赖 。
经典反例 :选课表

-
候选键是 (学号, 课程号)。
-
非主属性:姓名、系名、成绩。
-
依赖分析:
-
(学号, 课程号) → 成绩 ✅ 完全依赖。
-
学号 → 姓名 ❌ 部分依赖。
-
学号 → 系名 ❌ 部分依赖。
-
存在的问题 :
-
冗余 :张三的姓名和系名在两条记录中重复。
-
更新异常 :如果张三转到物理系,需要修改两行,否则数据矛盾。
-
插入异常 :新入学的学生还没选任何课程,无法插入他的姓名和系名(因为主键不能为空)。
-
删除异常 :如果S03只选了C003,退选后删除该行,则学生王五及其系信息全部丢失。
解决办法 :分解为两张表
学生表 (学号, 姓名, 系名)

成绩表 (学号, 课程号, 成绩)

现在每个表都满足2NF:学生表主键是学号,成绩表主键是 (学号, 课程号),所有非主属性都完全依赖主键。
3.第三范式(3NF)
定义 :在满足2NF的基础上,消除非主属性对候选键的传递函数依赖 。
反例 :在上一步的学生表中增加“系主任”字段
学生表(学号, 姓名, 系名, 系主任)

依赖关系:
-
学号 → 系名
-
系名 → 系主任
-
因此 学号 → 系主任(传递依赖)
存在的问题 :
-
冗余 :计算机系有3个学生,系主任“王教授”重复3次。
-
更新异常 :如果计算机系换系主任,需要修改所有计算机系学生的记录,否则出现不一致。
-
插入异常 :一个新成立的系(例如“数学系”)还没有学生,无法插入系主任信息。
-
删除异常 :如果物理系最后一个学生王五退学,删除了他的记录,那么物理系和系主任信息将彻底丢失。
解决办法 :进一步分解
学生表 (学号, 姓名, 系名)

系表 (系名, 系主任)

现在学生表不再包含系主任,系主任只存在于系表中,消除了传递依赖,满足3NF。
三、总结
