【数据库范式详解】1NF-BCNF

来源:互联网 发布:育知同创学费 编辑:程序博客网 时间:2024/06/06 02:17

关系型数据库,由于数据分别存储在不同的表中,因此设计不当就会造成严重的数据冗余。

而如果表的粒度设计得太小,又会放大关系型数据库读写很慢的缺点,表的连接操作会带来很大的开销。

因此在设计库表时,有1NF、2NF、3NF、BCNF、4NF、5NF这些范式,从前到后要求依次提高。

本文将介绍前4种范式及使用他们的原因,在实际设计时,一般要求满足3NF即可。

我的另外两篇文章:

《学生选课系统库表设计》

《网上商城开发阶段文档》

从ER图到表的创建,就是符合3NF范式的,大家在了解几个范式的原理后,可以参考。


1NF:(关系型数据库必须满足的范式)

定义:

表的设计中,每一列都应是原子的、不可再分。这里的不可再分也是相对的,例如地址这个属性,可以细粒度地把它当成复合属性来看,分为xx国xx省,也可以粗粒度的用一列来表示。

存在的问题:

假设一门课由一个老师教,一个学生可以选多门课。有选课表(学生id,学生姓名,学生性别,教师id,教师姓名,教师性别,课程id,课程名),显然该表满足1NF,但存在三种异常。

数据冗余:一门课程,n个学生选,则老师和课程的信息就重复存储了n次。

插入异常:假设想添加一条数据,以表示某学生入学。则有效的信息只有学生id,学生姓名,学生性别。其他位置必须用NULL值表示,否则无法进行insert操作。

删除异常:假设一批学生毕业了,要求清空他们的选课记录,则此时,如果不将学生的信息用NULL值表示,则所有的教师信息和课程信息也将会被清空。

更新异常:假设某个老师要更改自己教的课程名,则选了这门课的所有学生,课程名一列都需要更改。


因为1NF的不足,提出了2NF。

2NF:

定义:

表的设计中,不存在非superkey字段对任一candidate key字段的部分函数依赖(部分函数依赖指的是存在复合key中的某些字段决定非key字段的情况),也即所有非superkey字段都完全依赖于任意一组candidate key。

不满足2NF的例子:

假设某选课系统中存在如下函数依赖,其完全闭包F+={

(学号,课程名称) → (成绩,学分,姓名,年龄),①

(课程名称) → (学分),②

(学号) → (姓名, 年龄)  ③}

这个Fc的key是(学号,课程名称)。由于②和③都存在非关键字段对候选键的部分函数依赖(学分依赖候选键的一部分),所以它是不满足2NF的。

修改:

把选课关系表SelectCourse改为如下三个表:
学生:Student(学号, 姓名, 年龄);
课程:Course(课程名称, 学分);
选课关系:SelectCourse(学号, 课程名称, 成绩)。
这样的数据库表是符合第二范式的,一定程度上减少了数据冗余、更新异常、插入异常和删除异常。
另外,所有单关键字的数据库表都符合第二范式,因为不可能存在组合关键字。

满足2NF也会有数据冗余、插入/删除/更新异常的问题,因此提出了3NF。

3NF:

定义:

已知F+,则每张表涉及到的属性组成一个集合E,考虑它在F+上的函数依赖集,

若对任意的函数依赖 α-->β,满足如下三个条件之一,则称为该表的设计满足3NF。

①α-->β是一个平凡依赖(β∈α)

②α是key

③β-α的每一个属性A,都∈某一个该表的候选键,该条件等价于α不传递依赖于superkey

证明③的两个条件等价:

α不是superkey的情况下才会用到第三个条件来判断。则α必定是被某个superkey决定的属性,即有:γ->α   .而α->β,若β不属于候选键的一部分,那么就会出现γ->β的情况,这是不符合3NF的。但如果β是候选键的一部分,那么γ->β就是一个平凡依赖了。

3NF分解算法:
1.求出F对应的无冗余函数依赖集Fc,算法见我的博客《求正则覆盖&判断保持依赖》
2.对于Fc中的每个函数依赖,都将其作为一个子模式Ri
3.考察每个Ri,若存在至少一个Ri包含原关系R的candidate key则结束(存在一个schema包含一个key就行)
4.否则,将R的key作为一个新的子模式并入分解集中。结束。

首先,我们证明一下算法的正确性
    首先明确,所有模式的属性是在无冗余的Fc上求得的,因此,不可能存在非候选键的依赖传递:在分解的Ri上,如果有A->B,则AB都是无冗余的。如果存在B->C,且C是B的一部分,那么这与B无冗余矛盾。如果B->A的一部分,那么这是3NF所允许的,A-B是Ri的候选键之一。如果B->D且D不属于A,B,那么,这将是一个新的模式Rj。算法的保持依赖性和无损连接性证明略。
其次我们解释一下,为什么需要将候选键包含到分解后的模式中
    从分解算法中我们可以看到,对于所有的非冗余函数依赖,我们均将其作为一张表分解出去了。因此对于原来的所有函数依赖,必然是被保持的。但是,在属性上却有所遗漏。因为可能存在这样一个属性:他既不出现在函数依赖左边,也不出现在函数依赖右边。但他属于候选键的一部分,我们必须将他包含进分解的模式中,否则,会有属性的遗漏。  这样的属性必然存在于每一个候选键中(因为没有属性能决定这样的属性,所以必须由他自己来决定自己。) 
分解实例:


存在的问题:
还是选课系统的背景,假设一名老师只教一门课程;一门课程可由若干个不同的老师来教;每名学生选择一门课程就对应着一名老师。
根据语义可以得到F+ = {
(老师 → 课程),
(学生,课程)→老师,
(学生,老师) → 课程,
}
superkey为(老师,学生);(学生,课程)。将表设计为(老师,学生,课程)显然满足3NF。
但是,在该表中,由于老师可以确定课程,因此不同学生选了同一门课,在老师和课程这一栏就存在冗余。
此外,当一个老师开了一门课,但没有人选时,就必须使用学生是null来标识这个逻辑,有插入异常。

因此BCNF出现了,它的要求是3NF的前两条。

阅读全文
0 0
原创粉丝点击