1. 基本设计规范 构造数据 库 必 须 遵循一定的 规则 。在 关 系数据 库 中, 这种规则 就是范式。范式是符合某一 种级别 的 关 系模式的集合。 关 系数据 库 中的 关 系必 须满 足一定的要求,即 满 足不同的范式。目前 关 系数据 库 有六 种 范式:第一范
1. 基本设计规范
构造数据 库 必 须 遵循一定的 规则 。在 关 系数据 库 中, 这种规则 就是范式。范式是符合某一 种级别 的 关 系模式的集合。 关 系数据 库 中的 关 系必 须满 足一定的要求,即 满 足不同的范式。目前 关 系数据 库 有六 种 范式:第一范式( 1NF )、第二范式( 2NF )、第三范式( 3NF )、第四范式( 4NF )、第五范式( 5NF )和第六范式( 6NF )。 满 足最低要求的范式是第一范式( 1NF )。在第一范式的基 础 上 进 一 步满 足更多要求的称 为 第二范式( 2NF ),其余范式以次 类 推。一般 说 来,数据 库 只需 满 足第三范式( 3NF )就行了。下面我 们举 例介 绍 第一范式( 1NF )、第二范式( 2NF )和第三范式( 3NF )。
1.1 第一范式( 1NF )
在任何一个关系数据库中,第一范式( 1NF )是对关系模式的基本要求,不满足第一范式( 1NF )的数据库就不是关系数据库。所谓第一范式( 1NF )是指数据库表的每一列都是不可分割的基本数据项,同一列中不能有多个值,即实体中的某个属性不能有多个值或者不能有重复的属性。如果出现重复的属性,就可能需要定义一个新的实体,新的实体由重复的属性构成,新实体与原实体之间为一对多关系。在第一范式( 1NF )中表的每一行只包含一个实例的信息。例如,职工号,姓名,电话号码组成一个[员工信息表](一个人可能有一个办公室电话和一个家里电话号码),不能将员工信息都放在一列中显示,也不能将其中的两列或多列在一列中显示;员工信息表的每一行只表示一个员工的信息,一个员工的信息在表中只出现一次。
按照 1NF 采取三 种 方法进行规范:
一是重 复 存 储职 工号和姓名。 这样 , 关键 字只能是 电话 号 码 。
二是 职 工号 为关键 字, 电话 号 码 分 为单 位 电话 和住宅 电话 两个属性
三是 职 工号 为关键 字,但 强 制 每 条 记录 只能有一个 电话 号 码 。
以上三个方法,第一 种 方法最不可取,按 实际 情况 选 取后两 种 情况。
简 而言之,第一范式就是无重 复 的列。
1.2 第二范式( 2NF )
第二范式( 2NF )是在第一范式( 1NF )的基 础 上建立起来的,即 满 足第二范式( 2NF )必 须 先 满 足第一范式( 1NF )。第二范式( 2NF )要求数据 库 表中的 每 个 实 例或行必 须 可以被唯一地区分。 为实现 区分通常需要 为 表加上一个列,以存 储 各个 实 例的唯一 标识 。例如在 员 工信息表中加上了 员 工 编 号( emp_id )列,因 为每 个 员 工的 员 工 编 号是唯一的,因此 每 个 员 工可以被唯一区分。 这 个唯一属性列被称 为 主 关键 字或主 键 、主 码 。第二范式( 2NF )要求 实 体的属性完全依 赖 于主 关键 字。所 谓 完全依 赖 是指不能存在 仅 依 赖 主 关键 字一部分的属性,如果存在,那 么这 个属性和主 关键 字的 这 一部分 应该 分离出来形成一个新的 实 体,新 实 体与原 实 体之 间 是一 对 多的 关 系。 为实现 区分通常需要 为 表加上一个列,以存 储 各个 实 例的唯一 标识 。
例如:假定 选课关 系表 SelectCourse 包含 ( 学号 , 姓名 , 年 龄 , 课 程名称 , 成 绩 , 学分 ) 字段, 关键 字 为组 合 关键 字 ( 学号 , 课 程名称 ) ,因 为 存在如下决定 关 系:
( 学号 , 课 程名称 ) → ( 姓名 , 年 龄 , 成 绩 , 学分 )
这 个数据 库 表不 满 足第二范式,因 为 存在如下决定 关 系:
( 课 程名称 ) → ( 学分 )
( 学号 ) → ( 姓名 , 年 龄 )
即存在 组 合 关键 字中的部分字段决定非 关键 字的情况。
由于不符合 2NF , 这 个 选课关 系表会存在如下 问题 :
(1) 数据冗余:
同一 门课 程由 n 个学生 选 修, " 学分 " 就重 复 n-1 次;同一个学生 选 修了 m 门课 程,姓名和年 龄 就重 复 了 m-1 次。
(2) 更新异常:
若 调 整了某 门课 程的学分,数据表中所有行的 " 学分 " 值 都要更新,否 则 会出 现 同一 门课 程学分不同的情况。
(3) 插入异常:
假 设 要 开设 一 门 新的 课 程, 暂时还 没有人 选 修。 这样 ,由于 还 没有 " 学号 " 关键 字, 课 程名称和学分也无法 记录 入数据 库 。
(4) 删 除异常:
假 设 一批学生已 经 完成 课 程的 选 修, 这 些 选 修 记录 就 应该 从数据 库 表中 删 除。但是,与此同 时 , 课 程名称和学分信息也被 删 除了。很 显 然, 这 也会 导 致插入异常。
要解决上述问题把 选课关 系表 SelectCourse 改 为 如下三个表:
学生: Student( 学号 , 姓名 , 年 龄 ) ;
课 程: Course( 课 程名称 , 学分 ) ;
选课关 系: SelectCourse( 学号 , 课 程名称 , 成 绩 ) 。
这样 的数据 库 表是符合第二范式的,消除了数据冗余、更新异常、插入异常和 删 除异常。
另外,所有 单关键 字的数据 库 表都符合第二范式,因 为 不可能存在 组 合 关键 字。
简 而言之,第二范式就是非主属性非部分依 赖 于主 关键 字。
1.3 第三范式( 3NF )
满 足第三范式( 3NF )必 须 先 满 足第二范式( 2NF )。 在第二范式的基 础 上,数据表中如果不存在非 关键 字段 对 任一候 选关键 字段的 传递 函数依 赖则 符合第三范式。所 谓传递 函数依 赖 ,指的是如果存在 "A → B → C" 的决定 关 系, 则 C 传递 函数依 赖 于 A 。因此, 满 足第三范式的数据 库 表 应该 不存在如下依 赖关 系:
关键 字段 → 非 关键 字段 x → 非 关键 字段 y
例如:假定学生 关 系表 为 Student( 学号 , 姓名 , 年 龄 , 所在学院 , 学院地点 , 学院 电话 ) , 关键 字 为单 一 关键 字 " 学号 " ,因 为 存在如下决定 关 系:
( 学号 ) → ( 姓名 , 年 龄 , 所在学院 , 学院地点 , 学院 电话 )
这 个数据 库 是符合 2NF 的,但是不符合 3NF ,因 为 存在如下决定 关 系:
( 学号 ) → ( 所在学院 ) → ( 学院地点 , 学院 电话 )
即存在非 关键 字段 " 学院地点 " 、 " 学院 电话 " 对关键 字段 " 学号 " 的 传递 函数依 赖 。
它也会存在数据冗余、更新异常、插入异常和 删 除异常的情况, 读 者可自行分析得知。
把学生 关 系表分 为 如下两个表:
学生: ( 学号 , 姓名 , 年 龄 , 所在学院 ) ;
学院: ( 学院 , 地点 , 电话 ) 。
这样 的数据 库 表是符合第三范式的,消除了数据冗余、更新异常、插入异常和 删 除异常。
简 而言之,第三范式就是属性不依 赖 于其它非主属性。第三范式( 3NF )要求一个数据 库 表中不包含已在其它表中已包含的非主 关键 字信息。例如,存在一个部 门 信息表,其中 每 个部 门 有部 门编 号( dept_id )、部 门 名称、部 门简 介等信息。那 么 在
的 员 工信息表中列出部 门编 号后就不能再将部 门 名称、部 门简 介等与部 门 有 关 的信息再加入 员 工信息表中。如果不存在部 门 信息表, 则 根据第三范式( 3NF )也 应该 构建它,否 则 就会有大量的数据冗余。
1.4 鲍 依斯 - 科得范式( BCNF )
如果 关 系模式 R ( U , F )的所有属性(包括主属性和非主属性)都不 传递 依 赖 于 R 的任何候 选关键 字,那 么 称 关 系 R 是属于 BCNF 的。或是 关 系模式 R ,如果 每 个决定因素都包含 关键 字(而不是被 关键 字所包含), 则 RCNF 的 关 系模式。
例:配件管理 关 系模式 WPE ( WNO , PNO , ENO , QNT )分 别 表 仓库 号,配件号, 职 工号,数量。有以下条件:
a. 一个 仓库 有多个 职 工。
b. 一个 职 工 仅 在一个 仓库 工作。
c. 每 个 仓库 里一 种 型号的配件由 专 人 负责 ,但一个人可以管理几 种 配件。
d. 同一 种 型号的配件可以分放在几个 仓库 中。
分析:
由以上得 PNO 不能确定 QNT ,由 组 合属性( WNO , PNO )来决定,存在函数依 赖 ( WNO , PNO ) -> ENO 。由于 每 个 仓库 里的一 种 配件由 专 人 负责 ,而一个人可以管理几 种 配件,所以有 组 合属性( WNO , PNO )才能确定 负责 人,有( WNO , PNO ) -> ENO 。因 为 一个 职 工 仅 在一个 仓库 工作,有 ENO -> WNO 。由于 每 个 仓库 里的一 种 配件由 专 人 负责 ,而一个 职 工 仅 在一个 仓库 工作,有( ENO , PNO ) -> QNT 。
找一下候 选关键 字,因 为 ( WNO , PNO ) -> QNT ,( WNO , PNO ) -> ENO ,因此 ( WNO , PNO )可以决定整个元 组 ,是一个候 选关键 字。根据 ENO->WNO ,( ENO , PNO ) ->QNT ,故( ENO , PNO )也能决定整个元 组 , 为 另一个候 选关键 字。属性 ENO , WNO , PNO 均 为 主属性,只有一个非主属性 QNT 。它 对 任何一个候 选关键 字都是完全函数依 赖 的,并且是直接依 赖 ,所以 该关 系模式是 3NF 。
分析一下主属性。因 为 ENO->WNO ,主属性 ENO 是 WNO 的决定因素,但是它本身不是 关键 字,只是 组 合 关键 字的一部分。 这 就造成主属性 WNO 对 另外一个候 选关键 字( ENO , PNO )的部分依 赖 ,因 为 ( ENO , PNO ) -> ENO 但反 过 来不成立,而 P->WNO ,故( ENO , PNO ) -> WNO 也是 传递 依 赖 。
虽 然没有非主属性 对 候 选关键字 的 传递 依 赖 ,但存在主属性 对 候 选关键 字的 传递 依 赖 ,同 样 也会 带 来麻 烦 。如一个新 职 工分配到 仓库 工作,但 暂时处 于 实习阶 段,没有独立 负责对 某些配件的管理任 务 。由于缺少 关键 字的一部分 PNO 而无法插入到 该关 系中去。又如某个人改成不管配件了去 负责 安全, 则 在 删 除配件的同 时该职 工也会被 删 除。
解决 办 法:分成管理 EP ( ENO , PNO , QNT ), 关键 字是( ENO , PNO )工作 EW ( ENO , WNO )其 关键 字是 ENO
缺点:
分解后函数依 赖 的保持性 较 差。如此例中,由于分解 , 函数依 赖 ( WNO , PNO ) -> ENO 丢 失了 , 因而 对 原来的 语义 有所破坏。没有体 现 出 每 个 仓库 里一 种 部件由 专 人 负责 。有可能出 现 一部件由两个人或两个以上的人来同 时 管理。因此,分解之后的 关 系模式降低了部分完整性 约 束。
一个 关 系分解成多个 关 系,要使得分解有意 义 ,起 码 的要求是分解后不 丢 失原来的信息。 这 些信息不 仅 包括数据本身,而且包括由函数依 赖 所表示的数据之 间 的相互制 约 。 进 行分解的目 标 是达到更高一 级 的 规 范化程度,但是分解的同 时 必 须 考 虑 两个 问题 :无 损联 接性和保持函数依 赖 。有 时 往往不可能做到既有无 损联 接性,又完全保持函数依 赖 。需要根据需要 进 行 权 衡。
1.5 小 结 :
目地: 规 范化目的是使 结 构更合理,消除存 储 异常,使数据冗余尽量小,便于插入、 删 除和更新
原 则 :遵从概念 单 一化 " 一事一地 " 原 则 ,即一个 关 系模式描述一个 实 体或 实 体 间 的一 种联 系。 规 范的 实质 就是概念的 单 一化。
方法:将 关 系模式投影分解成两个或两个以上的 关 系模式。
要求:分解后的 关 系模式集合 应 当与原 关 系模式 " 等价 " ,即 经过 自然 联 接可以恢 复 原 关 系而不 丢 失信息,并保持属性 间 合理的 联 系。
注意:一个 关 系模式 结这 分解可以得到不同 关 系模式集合,也就是 说 分解方法不是唯一的。最小冗余的要求必 须 以分解后的数据 库 能 够 表达原来数据 库 所有信息 为 前提来 实现 。其根本目 标 是 节 省存 储 空 间 ,避免数据不一致性,提高 对关 系的操作效率,同 时满 足 应 用需求。 实际 上,并不一定要求全部模式都达到 BCNF 不可。有 时 故意保留部分冗余可能更方便数据 查询 。尤其 对 于那些更新 频 度不高, 查询频 度极高的数据 库 系 统 更是如此。
在 关 系数据 库 中,除了函数依 赖 之外 还 有多 值 依 赖 , 联 接依 赖 的 问题 ,从而提出了第四范式,第五范式等更高一 级 的 规 范化要求。在此,以后再 谈 。通常的数据 库设计 ,很少有人做到很符合以上几个范式的,一般 说 来,第一范式大家都可以遵守,完全遵守第二第三范式的人很少了,遵守的人一定就是 设计 数据 库 的高手了, BCNF 的范式出 现 机会 较 少,而且会破坏完整性,你可以在做 设计 之 时 不考 虑 它,当然在 ORACLE 中可通 过 触 发 器解决其缺点。以后我 们 共同做 设计 之 时 ,也希望大家遵守以上几个范式。