第一范式:
定义 :要求任何一张表必须有主键,每一个字段原子性不可再分。
例:
建立一个描述学校教务的数据库,该数据库涉及的对象包括学生的学号(Sno)、所在系(Sdept)、系主任姓名(Mname)、课程号(Cno)和成绩(Grade)。假设用一个单一的关系模式Student来表示,则该关系模式的属性为:U={Sno, Sdept, Mname, Cno, Grade}
得到一个函数依赖F,F={Sno—>Sdept, Sdept—>Mname, (Sno, Cno)—>Grade}
此时存在的问题:
(1)数据冗余:比如:每一个系的系主任姓名、所属系的信息重复出现;
(2)更新异常:比如:某个系更换系主任后,必须修改与该系学生有关的每一个条记录;
(3)插入异常:比如:一个系刚成立,暂时没有学生,则无法把这个系以及系主任的信息存入数据库;
(4)删除异常:比如:某个系的学生全部毕业了,则在删除该系学生信息的同时,这个系及系主任的信息也删除了。
分为三个关系模式:
S(Sno, Sdept, Sno—>Sdept);
SC(Sno, Cno, Grade, (Sno, Cno)—>Grade);
DEPT(Sdept, Mname, Sdept, Sdept—>Mname);
此时消除了插入异常、删除异常的问题,数据的冗余也得到了控制。
第二范式:
定义: 建立在第一范式的基础之上,要求所有非主键字段完全依赖主键,不要产生部分依赖。
例:
有关系模式S-L-C(Sno, Sdept, Sloc, Cno, Grade),分别是学生的学号(Sno)、所在系(Sdept)、学生的住处(Sloc)、课程号(Cno)、课程成绩(Grade),并且每个系的学生都住在同一个地方。S-L-C的主键是(Sno, Cno),则函数依赖有:
(Sno, Cno)—>Grade
Sno—>Sdept, (Sno, Cno)—>Sdept
Sno—>Sloc, (Sno, Cno)—>Sloc
Sdept—>Sloc(每个系的学生只住在一个地方)
可以看到非主键字段Sdept、Sloc并不完全依赖于主键,则S-L-C(Sno, Sdept, Sloc, Cno, Grade)不符合第二范式定义。
此时存在的问题:
(1)插入异常:比如:某个学生暂时没有选课,则该学生暂时无Cno,此时学生的其他信息也无法插入表中;
(2、删除异常:比如:学生A只选了一门课程号是a的课程,后面这门课他也不选了,这个a是主键,删除了a,A所在的整个记录也被删掉了;
(3)修改复杂:比如:某个学生从数学系(MA)转到了计算机系(CS),此时不但要修改所属系Sdept属性,还得修改住所Sloc属性。
分析可以发现,在于两类非主键字段,一类如Grade,它对主键是完全函数依赖;一类如Sdept、Sloc,它们对主键不是完全函数依赖。
将关系模式S-L-C(Sno, Sdept, Sloc, Cno, Grade)分解为两个关系模式:
SC(Sno, Cno, Grade) 和 S-L(Sno, Sdept, Sloc)
关系模式SC的主键是(Sno, Cno),关系模式S-L的主键是Sno。这样就是满足第二范式的非主键字段对主键字段完全函数依赖。
第三范式:
定义: 建立在第二范式的基础之上,要求所有非主键字段直接依赖主键,不要产生传递依赖。
例:
回顾前面第二范式中的例题,改写之后的关系模式,将关系模式S-L-C(Sno, Sdept, Sloc, Cno, Grade)分解为两个关系模式:SC(Sno, Cno, Grade) 和 S-L(Sno, Sdept, Sloc)。
关系模式S-L(Sno, Sdept, Sloc)中存在函数依赖,不符合第三范式的定义。
此时将S-L分解为:S-D(Sno, Sdept) 和 D-L(Sdept, Sloc)。分解之后的关系模式S-D与D-L中不再存在传递依赖。
设计数据库表的时候,按照以上的范式进行,可以避免表中数据冗余,空间的浪费。
总结
一对一:一对一,外键唯一
一对多:一对多,两张表,多的表加外键
多对多:多对多,三张表,关系表加两个外键
说明:数据库三大范式只是数据库表设计时的理论依据,最终的目的是为了满足客户的需求,有时候会选择用数据库表的冗余来换取执行速度,因为在查询过程中,多张表的连接会降低查询的效率。