数据库设计
函数依赖
A -> B,称为 B 依赖于 A ,也可以称为 A 决定 B。
例如:通过身份证号可以知道人的名字和住址,可以称为身份证号决定名字和住址。
身份证号 -> (名字,住址)
如果 A -> B,B -> C,则称为依赖的传递。
可能引发的数据库异常
如果在设计数据库字段的时候没有考虑全面,可能会造成一些异常:
- 数据冗余
- 修改异常
- 删除异常
- 插入异常
表一(反面例子):
student_id | class_id | student_name | class_name | grade |
---|---|---|---|---|
1 | 1 | 小李 | 一班 | 90 |
2 | 1 | 小王 | 一班 | 98 |
3 | 1 | 小潘 | 一班 | 89 |
1 | 2 | 小孙 | 二班 | 93 |
2 | 2 | 小霞 | 一班 | 95 |
数据冗余
表一中 student_id 代表学生在班里的学号。因为有多个同班的学生导致出现了 3 个一班和 2 个二班,造成了数据冗余。
修改异常
将 class_id 为 1 的班级的 class_name 修改为“三班”时,如果只修改了第一行的数据,第二行的 class_id 为 1 的班级的 class_name 还是一班,这就造成了修改异常。而如果为了防止这个异常将全部 class_id 为 1 的班级的 class_name 都修改一遍的话,效率将会十分低下。
删除异常
删除一个信息,那么也会丢失其它信息。例如删除了 班级1 需要删除第一行到第三行,那么 学生1-3 的信息就会丢失。
插入异常
在表一中如果要插入一个学生,这个学生如果没有成绩,就不能插入,产生插入异常。
范式(能有效解决异常)
1NF(第一范式)
第一范式中属性不可分。
表A:
student_id | class_id | student_name | class_name |
---|---|---|---|
(1,2,3) | 1 | (小李,小王,小潘) | 一班 |
(1,2) | 2 | (小孙,小霞) | 二班 |
表A中 student_id 和 student_name 都能分成更小的单位,这是不满足第一范式的,但是修改为上面表一的样子后,就满足第一范式了。
2NF(第二范式)
在满足第一范式的基础上,第二范式中每个非主属性完全函数依赖于键码。
例子还是表一,表一中键码为(class_id,student_id)。因为我们通过班级和学号就能确定一个具体的学号,比如一班一号就是小李、二班的一号就是小孙。满足第二范式的表应该是通过键码:(class_id,student_id)就能决定剩下的非主属性。但是在表一中班级名字 class_name 只依赖于班级编号 class_id,它是部分依赖于键码,而不是完全依赖键码(class_id,student_id)。
表一依赖关系:
class_id -> class_name
(class_id,student_id) -> student_name
于是我们可以通过分解成表B和表C,得到满足第二范式的表:
表B:
student_id | class_id | student_name | grade |
---|---|---|---|
1 | 1 | 小李 | 90 |
2 | 1 | 小王 | 98 |
3 | 1 | 小潘 | 89 |
1 | 2 | 小孙 | 93 |
2 | 2 | 小霞 | 95 |
表C:
class_id | class_name |
---|---|
1 | 一班 |
2 | 二班 |
3NF(第三范式)
在满足第二范式的基础上,非主属性不传递函数依赖于键码。
表D:
省 | 市 | 区 |
---|---|---|
广东 | 广州 | 越秀区 |
广东 | 广州 | 天河区 |
广东 | 广州 | 海珠区 |
四川 | 成都 | 温江区 |
四川 | 成都 | 成华区 |
在表D中,有如下的依赖关系:
省 -> 市
市 -> 区
同样产生了冗余的数据,只要进行拆分为表E和表F就能满足第三范式:
表E:
省 | 市 |
---|---|
广东 | 广州 |
四川 | 成都 |
表F:
市 | 区 |
---|---|
广州 | 越秀区 |
广州 | 天河区 |
广州 | 海珠区 |
成都 | 温江区 |
成都 | 成华区 |