规范化-数据库设计原则

规范化-数据库设计原则

规范化

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

数据冗余

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

规范化实例

为了说明方便,我们在本文中将使用一个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范式和四范式,但是这两种形式在商业应用中很少用到,在绝大多数情况下,三范式已经满足了数据库表规范化的要求,有效地解决了数据冗余和维护操作的异常问题。

结束语

在本文描述的过程中,我们通过结合实例的方法,通俗地演绎了数据表规范化的过程,并展示了在此过程中数据冗余、数据库操作异常等问题是如何得到解决的。

在具体的工程应用中,运用数据库规范化的方法来设计数据库表,将是具有现实意义的。

参考资料

Database Normalization Basics:数据库规范化基础原则

Normalization principles:数据库规范化原则和范式定义

来源:规范化-数据库设计原则

表规范化和非规范化的最佳实践

表规范化就是通过减少它的关系直到最简的表格。在建立一个逻辑上的关系型数据库设计中,表规范化是一个关键步骤。规范化有助于避免冗余和不一致的数据;它通常是一个逻辑上的数据模式练习,整个结果在物理设计中实现。

部署一个规范化设计有如下目标:

1. 消除冗余数据,例如在多个表中存储相同的数据。

2. 通过在表中只存储相关数据来强制有效数据的依赖,并把关系数据拆分到多个相关表中。

3. 将系统在数据结构和未来增长中的灵活性最大化

规范化

规范化的两到三个主要策略是:

1. 第三范式在在线交易处理(OLTP)和很多通用数据库中使用,包括企业数据仓库(也叫做原子仓库)。

2. 星型模式和雪花模式是规范化的三维模式,并且在数据仓库和 OLAP 中经常使用。

第三范式(3NF)

第三范式是由第一范式和第二范式中的规则组成的。下面是第三范式中的规则:

1. 消除重复组。为每个相关属性的集合建立单独的表,并为每个表建立一个主键。

2. 消除重复的列以及每个表中重复的数据。

3. 把应用到一个表中的多行柱形数据的子集,移动到不同的表中。

4. 在表之间用外键来建立联系

5. 消除那些与键值无关的列。如果属性不能解释一个键值,那就把它们移到另外一个表中。

6. 删除不依赖于主键的列

数据库设计中的第一范式,第二范式和第三范式

下面的图表显示了数据库设计的第一范式,第二范式,第三范式。

非规范化模式

非规范化模式

第一范式:

第一范式

为了让非规范化模式遵守第一范式,重复的数据元素组、客户地址行和客户名称被规范化到不同的表中。

第二范式:

第二范式

这个模式要遵守第二范式就必须遵守第一范式,并且所有属性必须完全依赖于一个组合键。

第三范式:

第三范式

这个模式要遵守第三范式就必须消除所有传递依赖。当一个在非键值域的值取决于另一个非键值域,也就是非组合键的一部分中的值时,就发生了传递依赖。

星型模式和雪花模式

星型模式和雪花模式在数据仓库 BI 系统中已经变得非常普遍。星型模式的基础是从它的维度中分离出系统的事实表。维度是作为数据的属性来定义的,比如 location 或 customer name、或部分描述、事实表参考和数据相关的具体时间。

例如,一个部分描述通常不会随着时间流逝而变化,因此它可以定义为一个维度。与此相反,部分每日销售是随时间变化的,因此是事实。之所以叫做星型模式是因为它的典型特点是一个保存了随着时间变化的大型的中央事实表,被一批维度表所环绕,其中存放着和事实表中事件条目的相关属性。

雪花模式简单的扩充了星型模式。在一个雪花模式设计中,较低基数的属性经常从星型模式中的一个维度表移动到另外一个维度表中,并将这两个维度表建立起关系。

非规范化

和规范化相比,非规范化是压缩表数目的处理过程,因此有可能增加数据库中的冗余数据。非规范化可以非常有效的减少复杂性或进行表连接的数目,并通过减少表的数目来减少数据库的复杂性。非规范化的主要目的是将一个系统的性能最大化并降低系统管理的复杂度。

索引设计最佳实践

索引对性能来说非常关键。数据库使用它们以达到下列目的:

1. 应用谓词来提高快速查询数据在数据库中的位置,减少查阅的行数。

2. 避免 ORDER BY 和 GROUP BY 子句产生的索引。

3. 引导连接的顺序。

4. 提供 index-only 的访问,这避免了访问数据页的成本。

5. 作为在关系数据库中强制唯一性约束的唯一方法。

然而,索引需要额外的硬件资源:

1. 它们增加 UPDATE、INSERT、DELETE 和 LOAD 操作的额外的 CPU 和 I/O 成本。

2. 它们会增加准备时间,因为它们为优化器提供更多的选择。

3. 他们会使用非常多的磁盘存储。

在 DB2 数据库系统中,一个 B+ 树结构被用作实现索引的底层结构。所有数据都存储在叶子节点,而且键值被随意的链接进一个双向方式中以提供双向的索引扫描。如果指定了 DISALLOW REVERSE SCANS,那么索引不能被反向扫描(不过物理上它是和一个 ALLOW REVERSE SCANS 索引是一样的)。

来源: DB2 最佳实践: 物理数据库设计最佳实践

范式是什么

范式是符合某一种级别的关系模式的集合。关系数据库中的关系必须满足一定的要求,即满足不同的范式。目前关系数据库有六种范式:第一范式(1NF)、第二范式(2NF)、第三范式(3NF)、第四范式(4NF)、第五范式(5NF)和第六范式(6NF)。满足最低要求的范式是第一范式(1NF)。在第一范式的基础上进一步满足更多要求的称为第二范式(2NF),其余范式以次类推。一般说来,数据库只需满足第三范式(3NF)就行了。

范式的原理

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

所谓第一范式(1NF)是指数据库表的每一列都是不可分割的基本数据项,同一列中不能有多个值,即实体中的某个属性不能有多个值或者不能有重复的属性。如果出现重复的属性,就可能需要定义一个新的实体,新的实体由重复的属性构成,新实体与原实体之间为一对多关系。在第一范式(1NF)中表的每一行只包含一个实例的信息。简而言之,第一范式就是无重复的列。

说明:在任何一个关系数据库中,第一范式(1NF)是对关系模式的基本要求,不满足第一范式(1NF)的数据库就不是关系数据库。

2. 第二范式(2NF)属性完全依赖于主键[消除部分子函数依赖]

第二范式(2NF)是在第一范式(1NF)的基础上建立起来的,即满足第二范式(2NF)必须先满足第一范式(1NF)。第二范式(2NF)要求数据库表中的每个实例或行必须可以被惟一地区分。为实现区分通常需要为表加上一个列,以存储各个实例的惟一标识。

例如员工信息表中加上了员工编号(emp_id)列,因为每个员工的员工编号是惟一的,因此每个员工可以被惟一区分。这个惟一属性列被称为主关键字或主键、主码。

第二范式(2NF)要求实体的属性完全依赖于主关键字。所谓完全依赖是指不能存在仅依赖主关键字一部分的属性,如果存在,那么这个属性和主关键字的这一部分应该分离出来形成一个新的实体,新实体与原实体之间是一对多的关系。为实现区分通常需要为表加上一个列,以存储各个实例的惟一标识。简而言之,第二范式就是属性完全依赖于主键。

3. 第三范式(3NF)属性不依赖于其它非主属性[消除传递依赖]

满足第三范式(3NF)必须先满足第二范式(2NF)。简而言之,第三范式(3NF)要求一个数据库表中不包含已在其它表中已包含的非主关键字信息。例如,存在一个部门信息表,其中每个部门有部门编号(dept_id)、部门名称、部门简介等信息。

那么在的员工信息表中列出部门编号后就不能再将部门名称、部门简介等与部门有关的信息再加入员工信息表中。如果不存在部门信息表,则根据第三范式(3NF)也应该构建它,否则就会有大量的数据冗余。简而言之,第三范式就是属性不依赖于其它非主属性。

范式的说明

第一范式:1NF是对属性的原子性约束,要求属性具有原子性,不可再分解;

通俗的理解是字段还可以再分吗?如过不能,则是符合1NF的设计。

第二范式:2NF是对记录的惟一性约束,要求记录有惟一标识,即实体的惟一性;

简单的解释,比如你和一个女生约会建立一张表,不用每条约会记录都记录她的身高、体重,将身高体重单独的存在一张表中供查询即可。

第三范式:3NF是对字段冗余性的约束,即任何字段不能由其他字段派生出来,它要求字段没有冗余。

打个比方,比如评论表,如果你将用户ID,用户头像都放在这留言表中,就是不合适的了。用户头像是依赖于用户ID,而不依赖该评论。

基本表的性质

基本表与中间表、临时表不同,因为它具有如下四个特性:

(1) 原子性。基本表中的字段是不可再分解的。

(2) 原始性。基本表中的记录是原始数据(基础数据)的记录。

(3) 演绎性。由基本表与代码表中的数据,可以派生出所有的输出数据。

(4) 稳定性。基本表的结构是相对稳定的,表中的记录是要长期保存的。

理解基本表的性质后,在设计数据库时,就能将基本表与中间表、临时表区分开来。

参考:

数据库设计范式的理解

数据库设计中的14个技巧

猜你喜欢

转载自justcoding.iteye.com/blog/1444565