数据库设计是数据库应用领域中的一个主要研究课题。数据库设计的任务是在给定的应用环境下,创建满足用户需求且性能良好的数据库模式,建立数据库及其应用系统,使之能有效地存储和管理数据,满足企业或单位各类用户的业务需求。
数据库设计需要一些理论作为指南,关系数据库规范化理论就是数据库设计的一个理论指南。规范化理论研究的是关系模式中各属性之间的依赖关系及其对关系模式性能的影响,探讨“好”的关系模式应该具备的性质,以及达到“好”的关系模式的方法。规范化理论为我们提供了判断关系模式好坏的理论标准,帮助我们预测可能出现的问题。规范化理论又是数据库设计人员的有力工具,同时也使数据库设计工作有了严格的理论基础。
本节主要讨论关系数据库规范化理论,即如何判断一个关系模式是否是好的关系模式,以及如何将不好的关系模式转换成好的关系模式。
数据的语义不仅表现为完整性约束,对关系模式的设计也提出了一定的要求。针对一个问题,需要解决的是如何构造一个合适的关系模式、应构造几个关系模式,以及每个关系模式由哪些属性组成等。这些都是数据库设计问题,确切地讲是关系数据库的逻辑设计问题。
下面首先介绍与函数依赖相关的一些内容。
函数依赖的基本概念
函数对我们来说已经是非常熟悉的概念了,对于公式,y=f(x),自然也不会陌生,但读者熟悉的大多是x和y之间数量上的对应关系,也就是给定一个x值,都会有一个y值和它对应,也可以说x函数决定y,或y函数依赖于x。在数据库中,函数依赖注重的是语义上的关系,例如:省=f(城市),只要给出一个具体的城市名称,就会有唯一一个省名称和它对应,如“武汉市”在“湖北省”,“城市”是自变量x,“省”是因变量或函数值y。这里把x函数决定y,或y函数依赖于x表示为:x→y。
由以上说明可以写出较直观的函数依赖定义:设有关系模式r(a1,a2,…,an),x和y为{a1,a2,…, an}的子集,如果对于r中的任意一个x值,都只有一个y值与之对应,则称x函数决定y,或y函数依赖于x。
例如,对于学生关系模式student,sno,sname,sdept,sage),有:sno→sname, sno→sdept, sno→sage。
对于学生选课关系模式sc(sno,cno,grade),有:(sno,cno)→grade。
显然,函数依赖讨论的是属性之间的依赖关系,它是语义范畴的概念,也就是说,关系模式的属性之间是否存在函数依赖只与语义有关。
一些术语和符号
下面给出经常用到的一些术语和符号。设有关系模式r(a1,a2,…,an),x和y均为{a1,a2,…, an}的子集。
1)如果x→y,但y不包含于x,则称x→y是非平凡的函数依赖。如不作特别说明,我们总是讨论非平凡函数依赖。
2)如果x→y,则称x为决定因子。
3)如果x→y,并且对于x的任意一个真子集x',都有x'—/→y,则称y完全函数依赖于x,记作x→y;如果x'→y成立,则称y部分函数依赖于x,记作x→y。
4)如果x→y、y→z,则称z传递函数依赖于x。
【例1-6】设有关系模式sc(sno,sname,cno,credit,grade),其中各属性含义分别为:学号、姓名、课程号、学分、成绩。该关系模式主键为(sno,cno)。
其函数依赖关系有:
sno→sname sname函数依赖于sno
(sno, cno)→sname sname部分函数依赖于(sno,cno)
(sno, cno)→grade grade完全函数依赖于(sno,cno)
【例1-7】设有关系模式s(sno,sname,dept,dept_master),其中各属性含义分别为:学号、姓名、所在系和系主任(假设一个系只有一个主任)。该关系模式的主键为sno。
sno→sname sname完全函数依赖于sno
由于:
sno→dept dept完全函数依赖于sno
dept→dept_master dept_master完全函数依赖于dept
所以有:
sno→dept_master dept_master传递函数依赖于sno
为什么要讨论函数依赖
讨论属性之间的关系以及函数依赖有什么必要呢?让我们通过例子来看一下。
设有描述学生选课及住宿情况的关系模式:
s-l-c(sno,sdept,sloc,cno,grade)
其中各属性的含义分别为:学号、学生所在系、学生所住公寓楼号、课程号和考试成绩。假设每个系的学生都住在一个公寓楼里。该关系模式的主键为(sno,cno)。
考虑一下这个关系模式存在什么问题。假设有如表1-4所示的数据。
从表1-4可以看出如下问题。
数据冗余问题:在这个关系中有关学生所在系和其所对应的公寓楼的信息有冗余,因为一个系有多少个学生,这个系所对应的公寓楼的信息就要重复存储多少遍。
数据更新问题:比如某一学生从计算机系转到了信息管理系,不但要修改此学生的sdept列的值,而且还要修改其sloc列的值,使修改复杂化。
数据插入问题:如果某个学生还没有选课,但已经有了sdept和sloc信息,我们也不能将此学生的这些已知信息插入到数据库中,因为cno为空,而cno为主属性,不能为空,因此不能插入该学生的其他基本信息。
数据删除问题:如果一个学生只选了一门课,而后来又不选了,则我们需要删除此学生选此门课的记录。但由于这个学生只选了一门课,删掉此学生选课记录的同时也删掉了此学生的其他基本信息。
类似的种种问题统称为操作异常。为什么会出现以上种种操作异常呢?因为这个关系模式没有设计好,在它的某些属性之间存在着“不良”的函数依赖。如何改造这个关系模式?克服以上种种问题,是我们这里要解决的问题,也是我们讨论函数依赖的原因。
解决上述种种问题的方法就是进行模式分解,即把一个关系模式分解成两个或多个关系模式,在分解的过程中消除那些“不良”的函数依赖,从而获得好的关系模式。关于模式分解将在后续章节介绍。
1. 范式
在上节已经看到了设计“不好”的关系模式所带来的问题,本节将讨论“好”的关系模式应具备的性质,即关系规范化问题。
关系数据库中的关系要满足一定的要求,满足不同程度要求的为不同的范式。满足最低要求的为第一范式,简称1nf(first normal form)。在第一范式中进一步满足一些要求的为第二范式,简称2nf,依此类推,还有3nf、bcnf、4nf和5nf。
所谓“第几范式”表示的是关系模式满足的条件,实际应用中,经常称某一关系模式为第几范式的关系模式。通常又把这个概念理解为符合某种条件的关系模式的集合,因此,r为第二范式的关系模式也可以写为:r∈2nf。
各种范式都是以对关系模式的属性间的函数依赖加以限制的形式表示的。这些范式是递进的:如果一个关系模式是1nf的,它比不是1nf的要好;同样,2nf的要比1nf的好,依此类推。这种方法的目的是让我们从一个关系模式或关系模式集合开始,逐步产生一个与初始集合等价的关系模式集合(即提供同样的信息)。范式越高、规范化的程度越高,关系模式就越好。
规范化的理论首先由e. f. codd于1971年提出,目的是设计“好的”关系数据库模式。关系规范化实际就是对有问题(操作异常)的关系模式进行分解以消除这些异常。
(1)第一范式
第一范式定义:不包含重复组的关系(即不包含非原子项的属性)是第一范式的关系。
如图1-12所示的关系就不是第一范式的,因为在这个关系中,“高级职称人数”不是基本的数据项,它是由两个基本数据项组成的一个复合数据项。非第一范式的关系转换成第一范式的关系非常简单,只需要将所有数据项都表示为不可再分的最小数据项即可。如图1-13所示的关系为将图1-12中的非第一范式关系转换为第一范式关系的情况。
(2)第二范式
第二范式定义:如果关系模式r∈1nf,并且r中的每个非主属性都完全函数依赖于主键,则r∈2nf。
从定义可以看出,所有主键只由一个列组成的1nf关系都是2nf的。但如果主键是由多个属性共同构成的复合主键,并且存在非主属性对主属性的部分函数依赖,则这个关系就不是2nf关系。
例如前文中提到的s-l-c(sno,sdept,sloc,cno,grade)就不是2nf的。
因为(sno,cno)是主键,而又有sno→sdept, 因此有:(sno,cno)→sdept。即存在非主属性对主键的部分函数依赖关系,所以,此s-l-c不是2nf的。前面我们已经发现这个关系存在操作异常,这些操作异常就是因为它存在部分函数依赖造成的。
可以用模式分解的办法将非2nf的关系模式分解为2nf的关系模式。
去掉部分函数依赖的分解过程如下:
1)对于组成主键的属性集合的每一个子集,用它作为主键构成一个关系模式。
2)对每个关系模式,将依赖于此主键的属性放置到此关系模式中。
3)去掉只由主键的子集构成的关系模式。
s-l-c关系模式分解后的形式为:s-l(sno,sdept,sloc)和s-c(sno,cno,grade)。
s-l的主键是(sno),并且有sno→sdept,sno→sloc,因此s-l已是2nf关系模式。
s-c的主键是(sno,cno),并且有(sno,cno)→grade,因此s-c也是2nf关系模式。
下面考虑一下分解到2nf之后,关系模式是否还会存在问题,先讨论s-l关系模式。
首先,在这个关系模式中,描述多少个学生就会重复描述每个系和其所在的公寓楼多少遍(见表1-5),因此还存在数据冗余。其次,当新成立一个系时,如果此系还没有招收学生,但已分配了公寓楼,则无法将此系的信息插入到s-l中,因为这时的学号为空。这是插入异常。
由此可以看出,第二范式的关系同样可能存在操作异常情况,因此需要对第二范式的关系模式进行进一步的分解。
(3)第三范式
第三范式定义:如果关系模式r∈2nf,并且所有非主属性都不传递函数依赖于主键,则r∈3nf。
从定义可以看出,如果存在非主属性对主键的传递函数依赖,则关系模式就不是3nf的。
先分析关系模式s-l(sno,sdept,sloc),因为有:sno→sdept,sdept→sloc,因此有:
sno→sloc
从对表1-5的分析已经知道,当关系模式中存在传递函数依赖时,这个关系模式仍然有操作异常,因此,还需要对其进行进一步的分解。
消除传递函数依赖的分解过程为:
1)对于不是候选键的每个决定因子,从表中删去依赖于它的所有属性。
2)新建一个关系模式,新关系模式中包含原关系模式中所有依赖于该决定因子的属性。
3)将决定因子作为新关系模式的主键。
s-l分解后的关系模式为:
s-d(sno,sdept)(主键为sno)和s-l(sdept,sloc)(主键为sdept)
对s-d,有sno→sdept,因此s-d是3nf的。
对s-l,有sdept→sloc,因此s-l也是3nf的。
由于3nf关系模式中不存在非主属性对主键的部分函数依赖和传递函数依赖,因而消除了很大一部分数据冗余和更新异常,因此,在通常的数据库设计中,一般要求将关系模式分解到3nf即可。
关系模式的分解准则
提高关系模式规范化程度,一般是通过把范式程度低的关系模式分解为若干个范式程度高的关系模式来实现的。每个规范化的关系模式应该只描述一个主题,如果某个关系模式描述了两个或多个主题,则应该将其分解为多个关系模式,使每个关系模式只描述一个主题。当发现一个关系模式存在操作异常时,通过把关系模式分解为两个或多个独立的关系模式,使每个关系模式只描述一个主题,就可以消除这些异常。
关系规范化的方法是进行模式分解,但分解后产生的关系模式应与原关系模式等价,即模式分解必须遵守一定的准则,不能表面上消除了操作异常现象,却留下了其他的问题。为此,模式分解要满足:
模式分解具有无损连接性。
模式分解能够保持函数依赖。
无损连接是指分解后的关系通过自然连接可以恢复成原来的关系,即通过自然连接得到的关系与原来的关系相比,既不多出信息,又不丢失信息。
保持函数依赖的分解是指在模式分解过程中,函数依赖不能丢失的特性,即模式分解不能破坏原来的语义。
为了得到更高范式的关系而进行的模式分解,是否总能既保证无损连接,又保持函数依赖呢?答案是否定的。我们这里对在分解过程中如何保持函数依赖以及如何保证无损连接不作讨论。一般情况下,在进行模式分解时,应将有直接依赖关系的属性放置在一个关系模式中,这样得到的分解结果一般能保证具有无损连接性,并且能保持函数依赖关系不变。