蓝海豹

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

http://baike.baidu.com/view/402020.htm

关系数据库设计范式

简介

设计关系数据库时,遵从不同的规范要求,设计出合理的关系型数据库,这些不同的规范要求被称为不同的范式,各种范式呈递次规范,越高的范式数据库冗余越小。
目前关系数据库有六种范式:第一范式(1NF)、第二范式(2NF)、第三范式(3NF)、巴德斯科范式(BCNF)、第四范式(4NF)和第五范式(5NF,又称完美范式)。满足最低要求的范式是第一范式(1NF)。在第一范式的基础上进一步满足更多规范要求的称为第二范式(2NF),其余范式以次类推。一般说来,数据库只需满足第三范式(3NF)就行了。

第一范式(1NF)无重复的列

所谓第一范式(1NF)是指在关系模型中,对域添加的一个规范要求,所有的域都应该是原子性的,即数据库表的每一列都是不可分割的原子数据项,而不能是集合,数组,记录等非原子数据项。即实体中的某个属性有多个值时,必须拆分为不同的属性。在符合第一范式(1NF)表中的每个域值只能是实体的一个属性或一个属性的一部分。简而言之,第一范式就是无重复的域。
说明:在任何一个关系数据库中,第一范式(1NF)是对关系模式的设计基本要求,一般设计中都必须满足第一范式(1NF)。不过有些关系模型中突破了1NF的限制,这种称为非1NF的关系模型。换句话说,是否必须满足1NF的最低要求,主要依赖于所使用的关系模型。

第二范式(2NF)属性

在1NF的基础上,非码属性必须完全依赖于码[在1NF基础上消除非主属性对主码的部分函数依赖]
第二范式(2NF)是在第一范式(1NF)的基础上建立起来的,即满足第二范式(2NF)必须先满足第一范式(1NF)。第二范式(2NF)要求数据库表中的每个实例或记录必须可以被唯一地区分。选取一个能区分每个实体的属性或属性组,作为实体的唯一标识。例如在员工表中的身份证号码即可实现每个一员工的区分,该身份证号码即为候选键,任何一个候选键都可以被选作主键。在找不到候选键时,可额外增加属性以实现区分,如果在员工关系中,没有对其身份证号进行存储,而姓名可能会在数据库运行的某个时间重复,无法区分出实体时,设计辟如ID等不重复的编号以实现区分,被添加的编号或ID选作主键。(该主键的添加是在ER设计时添加,不是建库时随意添加)
第二范式(2NF)要求实体的属性完全依赖于主关键字。所谓完全依赖是指不能存在仅依赖主关键字一部分的属性,如果存在,那么这个属性和主关键字的这一部分应该分离出来形成一个新的实体,新实体与原实体之间是一对多的关系。为实现区分通常需要为表加上一个列,以存储各个实例的唯一标识。简而言之,第二范式就是在第一范式的基础上属性完全依赖于主键。

第三范式(3NF)属性

在1NF基础上,任何非主属性不依赖于其它非主属性[在2NF基础上消除传递依赖]
第三范式(3NF)是第二范式(2NF)的一个子集,即满足第三范式(3NF)必须满足第二范式(2NF)。简而言之,第三范式(3NF)要求一个关系中不包含已在其它关系已包含的非主关键字信息。例如,存在一个部门信息表,其中每个部门有部门编号(dept_id)、部门名称、部门简介等信息。那么在员工信息表中列出部门编号后就不能再将部门名称、部门简介等与部门有关的信息再加入员工信息表中。如果不存在部门信息表,则根据第三范式(3NF)也应该构建它,否则就会有大量的数据冗余。简而言之,第三范式就是属性不依赖于其它非主属性,也就是在满足2NF的基础上,任何非主属性不得传递依赖于主属性。

巴德斯科范式(BCNF)属性

在1NF基础上,任何非主属性不能对主键子集依赖[在3NF基础上消除对主码子集的依赖]
巴德斯科范式(BCNF)是第三范式(3NF)的一个子集,即满足巴德斯科范式(BCNF)必须满足第三范式(3NF)。通常情况下,巴德斯科范式被认为没有新的设计规范加入,只是对第二范式与第三范式中设计规范要求更强,因而被认为是修正第三范式,也就是说,它事实上是对第三范式的修正,使数据库冗余度更小。这也是BCNF不被称为第四范式的原因。某些书上,根据范式要求的递增性将其称之为第四范式是不规范,也是更让人不容易理解的地方。而真正的第四范式,则是在设计规范中添加了对多值及依赖的要求。
对于BCNF,在主码的任何一个真子集都不能决定于非主属性。关系中U主码,若U中的任何一个真子集X都不能决定于非主属性Y,则该设计规范属性BCNF。例如:在关系R中,U为主码,A属性是主码中的一个属性,若存在A->Y,Y为非主属性,则该关系不属性BCNF。
一般关系型数据库设计中,达到BCNF就可以了!

范式应用实例剖析

下面以一个学校的学生系统为例分析说明,这几个范式的应用。首先第一范式(1NF):数据库表中的字段都是单一属性的,不可再分。这个单一属性由基本类型构成,包括整型、实数、字符型、逻辑型、日期型等。在当前的任何关系数据库管理系统(DBMS)中,傻瓜也不可能做出不符合第一范式的数据库,因为这些DBMS不允许你把数据库表的一列再分成二列或多列。因此,你想在现有的DBMS中设计出不符合第一范式的数据库都是不可能的。
首先我们确定一下要设计的内容包括那些。学号、学生姓名、年龄、性别、课程、课程学分、系别、学科成绩,系办地址、系办电话等信息。为了简单我们暂时只考虑这些字段信息。我们对于这些信息,说关心的问题有如下几个方面。
学生有那些基本信息
学生选了那些课,成绩是什么
每个课的学分是多少
学生属于那个系,系的基本信息是什么。

第二范式(2NF)实例分析

首先我们考虑,把所有这些信息放到一个表中(学号,学生姓名、年龄、性别、课程、课程学分、系别、学科成绩,系办地址、系办电话)下面存在如下的依赖关系。
(学号, 课程名称) → (姓名, 年龄, 成绩, 学分)
问题分析
因此不满足第二范式的要求,会产生如下问题
数据冗余:同一门课程由n个学生选修,"学分"就重复n-1次;同一个学生选修了m门课程,姓名和年龄就重复了m-1次。
更新异常:
1)若调整了某门课程的学分,数据表中所有行的"学分"值都要更新,否则会出现同一门课程学分不同的情况。
2)假设要开设一门新的课程,暂时还没有人选修。这样,由于还没有"学号"关键字,课程名称和学分也无法记录入数据库。
删除异常 :假设一批学生已经完成课程的选修,这些选修记录就应该从数据库表中删除。但是,与此同时,课程名称和学分信息也被删除了。很显然,这也会导致插入异常。
解决方案
把选课关系表SelectCourse改为如下三个表:
学生:Student(学号,姓名,年龄,性别,系别,系办地址、系办电话);
课程:Course(课程名称,学分);
选课关系:SelectCourse(学号,课程名称,成绩)。

第三范式(3NF)实例分析

接着看上面的学生表Student(学号,姓名,年龄,性别,系别,系办地址、系办电话),关键字为单一关键字"学号",因为存在如下决定关系:
(学号)→ (姓名,年龄,性别,系别,系办地址、系办电话
但是还存在下面的决定关系
(学号) → (所在系办)→(系办地点,系办电话)
即存在非关键字段"系办地点"、"系办电话"对关键字段"学号"的传递函数依赖。
它也会存在数据冗余、更新异常、插入异常和删除异常的情况。(数据的更新,删除异常这里就不分析了,可以参照2.1.1进行分析)
根据第三范式把学生关系表分为如下两个表就可以满足第三范式了:
学生:(学号,姓名,年龄,性别,系别);
系别:(系别,系办地址、系办电话)。

总结

上面的数据库表就是符合I,Ⅱ,Ⅲ范式的,消除了数据冗余、更新异常、插入异常和删除异常。
 
------------------------------------------------------------------------------------------------------------------------------------

<规范化-数据库设计>

序言

本文的目的就是通过详细的实例来阐述规范化的数据库设计原则。在DB2中,简洁、结构明晰的表结构对数据库的设计是相当重要的。规范化的表结构设计,在以后的数据维护中,不会发生插入(insert)、删除(delete)和更新(update)时的异常。反之,数据库表结构设计不合理,不仅会给数据库的使用和维护带来各种各样的问题,而且可能存储了大量不需要的冗余信息,浪费系统资源。

要设计规范化的数据库,就要求我们根据数据库设计范式――也就是数据库设计的规范原则来做。但是一些相关材料上提到的范式设计,往往是给出一大堆的公式,这给设计者的理解和运用造成了一定的困难。因此,本文将结合具体形象的例子,尽可能通俗化地描述三个范式,以及如何在实际工程中加以优化应用。


规范化

在设计和操作维护数据库时,关键的步骤就是要确保数据正确地分布到数据库的表中。使用正确的数据结构,不仅便于对数据库进行相应的存取操作,而且可以极大地简化应用程序的其他内容(查询、窗体、报表、代码等)。正确进行表设计的正式名称就是"数据库规范化"。后面我们将通过实例来说明具体的规范化的工程。关于什么是范式的定义,请参考附录文章 1.


数据冗余

数据应该尽可能少地冗余,这意味着重复数据应该减少到最少。比如说,一个部门雇员的电话不应该被存储在不同的表中, 因为这里的电话号码是雇员的一个属性。如果存在过多的冗余数据,这就意味着要占用了更多的物理空间,同时也对数据的维护和一致性检查带来了问题,当这个员工的电话号码变化时,冗余数据会导致对多个表的更新动作,如果有一个表不幸被忽略了,那么就可能导致数据的不一致性。


规范化实例

为了说明方便,我们在本文中将使用一个SAMPLE数据表,来一步一步分析规范化的过程。

首先,我们先来生成一个的最初始的表。


CREATE TABLE "SAMPLE" (
		  "PRJNUM" INTEGER NOT NULL, 
		  "PRJNAME" VARCHAR(200), 
		  "EMYNUM" INTEGER NOT NULL,
		  "EMYNAME" VARCHAR(200), 
		  "SALCATEGORY" CHAR(1), 
		  "SALPACKAGE" INTEGER)   
		 IN "USERSPACE1";

ALTER TABLE "SAMPLE" 
	ADD PRIMARY KEY
		("PRJNUM", "EMYNUM");

Insert into SAMPLE(PRJNUM, PRJNAME, EMYNUM, EMYNAME, SALCATEGORY, SALPACKAGE)
values(100001, 'TPMS', 200001, 'Johnson', 'A', 2000), (100001, 'TPMS', 200002,
'Christine', 'B', 3000), (100001, 'TPMS', 200003, 'Kevin', 'C', 4000), (100002,
'TCT', 200001, 'Johnson', 'A', 2000), (100002, 'TCT', 200004, 'Apple', 'B',
3000);
表1-1
表1-1

考察表1-1,我们可以看到,这张表一共有六个字段,分析每个字段都有重复的值出现,也就是说,存在数据冗余问题。这将潜在地造成数据操作(比如删除、更新等操作)时的异常情况,因此,需要进行规范化。


第一范式

参照范式的定义,考察上表,我们发现,这张表已经满足了第一范式的要求。

1、因为这张表中字段都是单一属性的,不可再分;

2、而且每一行的记录都是没有重复的;

3、存在主属性,而且所有的属性都是依赖于主属性;

4、所有的主属性都已经定义

事实上在当前所有的关系数据库管理系统(DBMS)中,都已经在建表的时候强制满足第一范式。因此,这张SAMPLE表已经是一张满足第一范式要求的表。考察表1-1,我们首先要找出主键。可以看到,属性对<Project Number, Employee Number>是主键,其他所有的属性都依赖于该主键。


从一范式转化到二范式

根据第二范式的定义,转化为二范式就是消除部分依赖。

考察表1-1,我们可以发现,非主属性<Project Name>部分依赖于主键中的<Project Number>; 非主属性<Employee Name>,<Salary Category>和<Salary package>都部分依赖于主键中的<Employee Number>;

表1-1的形式,存在着以下潜在问题:

1. 数据冗余:每一个字段都有值重复;

2. 更新异常:比如<Project Name>字段的值,比如对值"TPMS"了修改,那么就要一次更新该字段的多个值;

3. 插入异常:如果新建了一个Project,名字为TPT, 但是还没有Employee加入,那么<Employee Number>将会空缺,而该字段是主键的一部分,因此将无法插入记录;

Insert into SAMPLE(PRJNUM, PRJNAME, EMYNUM, EMYNAME, SALCATEGORY, SALPACKAGE) values(100003, 'TPT', NULL, NULL, NULL, NULL)
 
4. 删除异常:如果一个员工 200003, Kevin 离职了,要将该员工的记录从表中删除,而此时相关的Salary信息 C 也将丢失, 因为再没有别的行纪录下 Salary C的信息。

Delete from sample where EMYNUM = 200003
Select distinct SALCATEGORY, SALPACKAGE from SAMPLE

因此,我们需要将存在部分依赖关系的主属性和非主属性从满足第一范式的表中分离出来,形成一张新的表,而新表和旧表之间是一对多的关系。由此,我们得到:

CREATE TABLE "PROJECT" (
		  "PRJNUM" INTEGER NOT NULL, 
		  "PRJNAME" VARCHAR(200))
		 IN "USERSPACE1";

ALTER TABLE "PROJECT" 
	ADD PRIMARY KEY
		("PRJNUM");

Insert into PROJECT(PRJNUM, PRJNAME) values(100001, 'TPMS'), (100002, 'TCT');

表1-2
表1-2 
表 1-3

CREATE TABLE "EMPLOYEE" (
		  "EMYNUM" INTEGER NOT NULL, 
		  "EMYNAME" VARCHAR(200), 
"SALCATEGORY" CHAR(1), 
"SALPACKAGE" INTEGER)
		 IN "USERSPACE1";

ALTER TABLE "EMPLOYEE" 
	ADD PRIMARY KEY
		("EMYNUM");

Insert into EMPLOYEE(EMYNUM, EMYNAME, SALCATEGORY, SALPACKAGE) values(200001,
'Johnson', 'A', 2000), (200002, 'Christine', 'B', 3000), (200003, 'Kevin', 'C',
4000), (200004, 'Apple', 'B', 3000);

Employee Number	Employee Name	Salary Category	Salary Package
200001	Johnson	A	2000
200002	Christine	B	3000
200003	Kevin	C	4000
200004	Apple	B	3000

 

CREATE TABLE "PRJ_EMY" (
		  "PRJNUM" INTEGER NOT NULL, 
		  "EMYNUM" INTEGER NOT NULL)
		 IN "USERSPACE1";

ALTER TABLE "PRJ_EMY" 
	ADD PRIMARY KEY
		("PRJNUM", "EMYNUM");

Insert into PRJ_EMY(PRJNUM, EMYNUM) values(100001, 200001), (100001, 200002),
(100001, 200003), (100002, 200001), (100002, 200004);

同时,我们把表1-1的主键,也就是表1-2和表1-3的各自的主键提取出来,单独形成一张表,来表明表1-2和表1-3之间的关联关系:

表 1-4
表 1-4

这时候我们仔细观察一下表1-2, 1-3, 1-4, 我们发现插入异常已经不存在了,当我们引入一个新的项目 TPT 的时候,我们只需要向表1-2 中插入一条数据就可以了, 当有新人加入项目 TPT 的时候,我们需要向表1-3, 1-4 中各插入一条数据就可以了。虽然我们解决了一个大问题,但是仔细观察我们还是发现有问题存在。


从二范式转化到三范式

考察表前面生成的三张表,我们发现,表1-3存在传递依赖关系,即:关键字段< Employee Number > --> 非关键字段< Salary Category > -->非关键字段< Salary Package >。而这是不满足三范式的规则的,存在以下的不足:

1、 数据冗余:<Salary Category>和<Salary Package>的值有重复;

2、 更新异常:有重复的冗余信息,修改时需要同时修改多条记录,否则会出现数据不一致的情况;

3、 删除异常:同样的,如果员工 200003 Kevin 离开了公司,会直接导致 Salary C 的信息的丢失。

Delete from EMPLOYEE where EMYNUM = 200003
Select distinct SALCATEGORY, SALPACKAGE from EMPLOYEE

因此,我们需要继续进行规范化的过程,把表1-3拆开,我们得到:

表 1-5
表 1-5

和表 1-6
表 1-6 
这时候如果 200003 Kevin 离开公司,我们只需要从表 1-5 中删除他就可以了, 存在于表1-6中的Salary C信息并不会丢失。但是我们要注意到除了表 1-5 中存在 Kevin 的信息之外, 表1-4中也存在 Kevin 的信息, 这很容易理解, 因为 Kevin 参与了项目 100001, TPMS, 所以当然也要从中删除。

至此,我们将表1-1经过规范化步骤,得到四张表,满足了三范式的约束要求,数据冗余、更新异常、插入异常和删除异常。

在三范式之上,还存在着更为严格约束的BC范式和四范式,但是这两种形式在商业应用中很少用到,在绝大多数情况下,三范式已经满足了数据库表规范化的要求,有效地解决了数据冗余和维护操作的异常问题。

 
posted on 2013-03-25 17:02  蓝海豹  阅读(293)  评论(0编辑  收藏  举报