目前关系数据库有六种范式。满足其中一个级别的范式总是在满足比他级别低的范式基础上进行判断的。一般来说数据库只需满足第三范式(1NF)就行了。
在看三范式之前,先看几个定义:函数依赖、部分函数依赖、完全函数依赖、传递函数依赖。
完全函数依赖:在R(U)中,如果X→Y,并且对于X的任何一个真子集X' ,都没有X'→ Y,则称Y对X完全函数依赖。
部分函数依赖:在R(U)中,如果X→Y,但Y不完全依赖于X,则称Y对X部分函数依赖。
传递函数依赖:设X,Y,Z是关系R(U)中互不相同的属性集合,存在X→Y,Y→Z,且X没有函数依赖于Y或Z,则称Z传递函数依赖于X。
函数依赖: 设X,Y是关系R(U)的两个属性集合,当任何时刻R(U)中的任意两个元组中的X属性值相同时,则它们的Y属性值也相同,则称X函数决定Y,或Y函数依赖于X。
现在我们就可以去看三范式究竟都是什么了。
第一范式(1NF):数据库表的每一列都是不可分割的基本数据项,同一列中不能有多个值,即实体中的某个属性不能有多个值或者不能有重复的属性。要求数据是原子级别的,不可再分。只要是关系数据库,必满足1NF。
第二范式(2NF):在满足第一范式的基础上,实体的每个非主键属性完全函数依赖于主键属性。
第三范式(3NF):在满足第二范式的基础上,在实体中不存在非主键属性传递函数依赖于主键属性。
1NF是确保是关系型数据库,2NF是消除部分函数依赖,3NF是消除传递函数依赖。
下面我们来看一个数据库设计的例子(ps:是课程中的例子)
MEMBER_BOOK(MemNo, Book_Id, DueDate, Mname, City, CallNo, Title)
PK
MEMBER_BOOK |
MemNo | Mname | City | CallNo | Title | Book_ID | DueDate |
---|---|---|---|---|---|---|
3 | Avi | PGH | 301 | DBS | 84 | 10/12/99 |
5 | Susan | NYC | 500 | OS | 50 | 11/12/99 |
2 | Rajiv | LONDON | 20 | AI | 20 | 01/06/00 |
5 | Susan | NYC | 400 | PL | 85 | 12/12/99 |
5 | Susan | NYC | 301 | DBS | 86 | 02/29/00 |
很明显,上述关系存在数据冗余,因为数据冗余,会导致以下更新异常:
a. 修改异常 如果修改一个人所在的城市,那就要修改多处,比如修改Susan的City,如果修改时漏了一项将会导致数据一个人同时在两地居住的情况。
b. 插入异常 如果有新成员加入,但他没借书,因为和书籍相关的字段有不能为空的,所以必须等他借了书之后才能插入。
c. 删除异常 如果有个成员,只借了一本书,然后去还书,这个成员会从这个表中消失。但其实他还是个成员,只不过没借书而已。
上表中所存在以下函数依赖,其中fd1-fd5是单个属性的依赖,fd6-fd10左端是多个属性:
fd1: MemNo -> Mname fd2: MemNo -> City fd3: CallNo -> Title fd4: Book_Id -> CallNo fd5: Book_Id -> Title fd6: {MemNo, Book_Id} -> DueDate fd7: {MemNo, Book_Id} -> Mname fd8: {MemNo, Book_Id} -> City fd9: {MemNo, Book_Id} -> CallNo fd10: {MemNo, Book_Id} -> Title
我们可以把上面的改变压缩一下,压缩之后的如下依赖,其中FD0是fd6-fd10压缩而成的,FD1是fd1和fd2压缩而成的,FD3是fd4和fd5压缩而成的:
FD0: {MemNo, Book_Id} -> {DueDate, Mname, City, CallNo, Title} FD1: MemNo -> {Mname, City} FD2: CallNo -> {Title} FD3: Book_Id -> {CallNo, Title}
下面用三范式对上述关系进行改进:
很明显的看出,符合1NF。
根据2NF,消除部分函数依赖后,如下:
MEMBER(MemNo, Mname, City) PK FD1: MemNo -> {Mname, City} BOOK(Book_Id, CallNo, Title) PK FD3: Book_Id -> {CallNo, Title} FD2: CallNo -> {Title} BORROW(MemNo, Book_ID, DueDate) PK FD4: {MemNo, Book_ID} -> {DueDate}
然后根据3NF,BOOK消除传递函数依赖后,如下:
NEWBOOK(Book_Id, CallNo) PK FD3: Book_Id -> {CallNo} TITLE (CallNo, Title) PK FD2: CallNo -> {Title}
需要注意的是3NF是在2NF的基础上,是循序渐进的。