
1.4 关系数据库
1.4.1 关系模型
关系模型由关系数据结构、关系操作集合和关系完整性约束3部分组成。
1. 关系数据结构
在用户观点下,关系模型中数据的逻辑结构是一张二维表,它由行和列组成。例如,表1-1所示的学生信息表。
(1)关系(Relation):一个关系对应于一张二维表,每个关系都有一个关系名。表1-1所示的学生信息表可以取名为“学生信息”。
表1-1 学生信息

(2)元组(Tuple):表中的一行称为一个元组,对应于存储文件中的一个记录。
(3)属性(Attribute):表中的一列称为一个属性,给每个属性起一个名字,称为属性名。属性对应于存储文件中的字段。
(4)候选码(Candidate key):如果在一个关系中,存在多个属性(或属性组合)都能用来唯一标识该关系的元组,这些属性(或属性组合)都称为该关系的候选码(或候选关键字)。例如,假设以上“学生信息”关系中的姓名没有重名现象,则学号和姓名都是候选码。
(5)主码(Primary key):在一个关系的若干个候选码中指定作为码的属性(或属性组合)称为该关系的主码(或主关键字)。例如,可以将以上“学生信息”关系的学号作为该关系的主码。
(6)主属性(Primary Attribute):包含在候选码中的属性称为主属性。例如,学号和姓名(假设无重名)都是主属性。
(7)非主属性(Nonprimary Attribute):不包含在任何候选码中的属性称为非码属性或非主属性。例如,性别和年龄都是非主属性。
(8)关系模式(Relation Schema):对关系的描述称为关系模式,一般表示如下。
关系名(属性1,属性2,…,属性n)
例如,以上的“学生信息”表是一个关系,其关系模式如下。
学生信息(学号,姓名,性别,年龄)
(9)全码(All-key):如果一个关系模型的所有属性一起构成这个关系的码,则称其为全码。
(10)域(Domain):属性的取值范围称为域。例如,性别的域是(男,女),年龄的域是大于0的整数。
(11)分量(Component):元组中的一个属性值称为分量。例如,表1-1中的“李明”。
在关系模型中,实体和实体之间的联系都是用关系来表示的。例如,图1-12所表示的概念模型中的学生、课程和选修关系可以表示为以下3个关系模式。
学生信息(学号,姓名,性别,年龄) 课程(课程号,课程名,学分) 选修(学号,课程号,成绩)
2. 关系操作
关系操作主要包括查询、插入、修改和删除数据,这些操作的操作对象和操作结果都是关系,也就是元组的集合。
3. 关系的完整性约束
关系的完整性约束主要包括3类:实体完整性、参照完整性和用户定义的完整性。其中,实体完整性和参照完整性是关系模型必须满足的完整性约束条件,用户定义的完整性是指针对具体应用需要自行定义的约束条件。
(1)实体完整性
一个基本关系通常对应于现实世界的一个实体集。例如,学生关系对应于学生的集合。现实世界中的实体是可区分的,即它们具有某种唯一性标识。相应地,关系模型中以主码作为唯一性标识。主码中的属性即主属性不能取空值。所谓空值就是“不知道”或“无意义”的值。如果主属性取空值,就说明存在某个不可标识的实体,即存在不可区分的实体,这与现实世界的应用环境相矛盾,因此这个实体一定不是一个完整的实体。这就是实体的完整性规则。
实体完整性定义:若属性A是基本关系R的主属性,则属性A不能取空值。
(2)参照完整性
在关系模型中,实体及实体间的联系都是用关系来描述的,这样就需要在关系与关系之间通过某些属性建立起它们之间的联系。
例如,对于“部门”实体和“职工”实体,可以用下面的关系来表示。
职工(职工编号,职工姓名,年龄,性别,部门编号) 部门(部门编号,部门名称)
这两个关系之间通过“部门编号”属性建立了联系。显然,职工关系中的“部门编号”值必须是确实存在的部门的部门编号,即在部门关系中要有该记录。也就是说,职工关系中的“部门编号”属性的取值需要参照部门关系的“部门编号”的属性取值。这里称职工关系引用了部门关系的主码“部门编号”。
又如,对于以下3个关系模式:
学生信息(学号,姓名,性别,年龄) 课程(课程号,课程名,学分) 选修(学号,课程号,成绩)
“学生信息”关系的主码是学号,“课程”关系的主码是课程号,而“选修”关系的主码是(学号,课程号),选修关系中的学号必须是一个在学生信息关系中存在的学号,而选修关系中的课程号也必须是一个在课程关系中存在的课程号。
参照完整性定义:设F是基本关系R的一个或一组属性,但不是关系R的码,如果F与基本关系S的主码Ks相对应,则称F是基本关系R的外码(Foreign Key),并称基本关系R为参照关系(Referencing Relation),基本关系S为被参照关系(Referenced Relation)。关系R和S不一定是不同的关系。
例如,设有以下两个关系模式:
职工(职工编号,姓名,性别,部门编号) 部门(部门编号,名称,地址,简介)
在“职工”关系中,主码为职工编号,在“部门”关系中,主码为部门编号,所以“职工”关系中的部门编号是该关系的外码。
这里,“职工”关系为参照关系,“部门”关系为被参照关系。
参照完整性规则:若属性(或属性组)F是基本关系R的外码,它与基本关系S的主码Ks相对应(基本关系R和S不一定是不同的关系),则对于R中每个元组在F上的值有如下要求。
• 或者取空值(F的每个属性值均为空值)。
• 或者等于S中某个元组的主码值。
参照完整性规则就是定义外码与主码之间的引用规则。
(3)用户定义的完整性
实体完整性和参照完整性适用于任何关系数据库系统。除此之外,不同的关系数据库系统根据其应用环境的不同,往往还需要一些特殊的约束条件。用户定义的完整性就是针对某一具体应用所涉及的数据必须满足的语义要求,对关系数据库中的数据定义的约束条件。关系模型应提供定义和检验这类完整性的机制,以便用统一的系统的方法处理它们,而不要由应用程序承担这一功能。
4. 对关系的限制
上面提到,在用户观点下,关系模型中数据的逻辑结构是一张二维表,但并不是所有的二维表都是关系,关系数据库对关系是有一定限制的,归纳起来有以下几个方面。
(1)表中的每一个数据项必须是单值的,每一个属性必须是不可再分的基本数据项。这是关系数据库对关系最基本的限制。例如,表1-2就是一个不满足该要求的表,因为工资不是最小的数据项,它还可以再分解为基本工资、职务工资和工龄工资。
表1-2 具有可再分割属性的表

(2)每一列中的数据项具有相同的数据类型,来自同一个域。
(3)每一列的名称在一个表中是唯一的。
(4)列次序可以是任意的。
(5)表中的任意两行(即元组)不能相同。
(6)行次序可以是任意的。
1.4.2 关系数据库的规范化理论
数据库设计的问题可以简单描述为,如果要把一组数据存储到数据库中,如何为这些数据设计一个合适的逻辑结构呢?如在关系数据库系统中,针对一个具体问题,应该构造几个关系?每个关系由哪些属性组成?使数据库系统无论是在数据存储方面,还是在数据操纵方面都有较好的性能。这就是关系数据库规范化理论要研究的主要问题。
E-R模型的方法讨论了实体与实体之间的数据联系,而关系规范化理论主要讨论实体内部属性与属性之间的数据的联系,其目标是要设计一个“好”的关系数据库模型。
1. 问题的提出
设有以下关系“学生”:
学生(学号,姓名,性别,课程号,课程名称,成绩)
该关系表示了学生选修各门课程的成绩。假设记录内容见表1-3。
表1-3 “学生”关系

可以看出,“学生”关系的主码应为(学号,课程号)。该关系存在以下问题。
(1)数据冗余
所谓数据冗余就是数据的重复出现。当一个学生选修多门课程时,学生信息重复出现,导致了数据冗余的现象。例如,以上关于“林梅”的信息就出现了3次;同样,一门课程有多个学生选修,也导致了该课程信息的冗余。例如,“数据库应用”课程被“林梅”和“张军”选修了,因此,其课程名称出现了2次。显然,数据冗余会导致数据库存储性能的下降,同时还会带来数据的不一致性问题。
(2)不一致性
由于存在着数据冗余,因此,如果某个数据需要修改,则可能会因为其多处存在而导致在修改时不能全部修改过来,产生数据的不一致。例如,假设有40名学生选修了“数据库应用”这门课,则在关系表中就会有40条记录包含有课程名称“数据库应用”,如果该课程名称需要改成“数据库原理及应用”,则可能会只修改了其中的一些记录,而其他记录没有修改。这就是数据的不一致,也叫更新异常。
(3)插入异常
如果新生刚刚入校,还没有选修课程,则学生信息就无法插入到表中,因为课程号为空,而主码为(学号,课程号),根据关系模型的实体完整性规则,主码不能为空或部分为空,因此无法插入新生数据,这就是插入异常。又如,学校计划下学期开一门新课“计算机组成原理”,该课程信息也不能马上添加到表中,因为还没有学生选修该课程,无法知道学生的信息。简单来说,插入异常就是该插入的数据不能正常插入。
(4)删除异常
当学生毕业时,需要删除相关的学生记录,于是就会删除对应的课程号、课程名信息。这就是删除异常。例如,在“学生”关系表中要删除学生记录(060103,赵心,女,003,管理信息系统,85),则会丢失课程号为“003”、课程名为“管理信息系统”的课程信息。简单来说,删除异常就是不该删除的数据被异常地删除了。
为了克服以上问题,可以将“学生”关系分解为如下的3个关系。
学生基本信息(学号,姓名,性别) 主码为学号 课程(课程号,课程名称) 主码为课程号 选修(学号,课程号,成绩) 主码为(学号,课程号)
首先,这样分解后的关系在一定程度上解决了数据冗余。例如,如果一门课程被100个学生选修,则该课程名称在“课程”关系中只会出现一次(在选修关系中只需要存储这100名学生的学号和该课程的课程号及成绩信息,但课程名称不会重复出现)。数据的不一致性是由于数据冗余产生的,解决了数据的冗余问题,不一致性问题就自然解决了。
其次,由于学生基本信息和课程信息是分开存储的,如果新生刚刚入校,也可以将新生信息插入到“学生基本信息”关系中,只是在“选修”关系中没有该学生的相应成绩记录,因此不存在插入异常问题。
同样,当学生毕业时,要删除相关的学生信息,则只需要删除“学生基本信息”关系中的相关记录和“选修”关系中的相关成绩记录,不会删除课程信息,因此解决了删除异常的问题。
为什么对“学生”关系进行以上分解之后,可以消除所有异常呢?这是因为“学生”关系中的某些属性之间存在数据依赖,这种数据依赖会造成数据冗余、插入异常、删除异常等问题。数据依赖是对属性间数据的相互关系的描述。
2. 函数依赖
函数依赖是数据依赖的一种描述形式。
定义1 设R(U)是属性集U上的关系模式。X,Y是U的子集。如果对于R(U)的任意一个可能的关系r,r中不可能存在两个元组在X上的属性值相等,而在Y上的属性值不等,则称“X函数确定Y”或“Y函数依赖于X”,记作X→Y。
简单地说,如果属性X的值决定属性Y的值(如果知道X的值就可以获得Y的值),则属性Y函数依赖于属性X。
若X→Y,并且Y→X,则记为X←→Y。
若Y函数不依赖于X,则记为XY。
例如,设有以下关系模式:
商品(商品名称,价格)
如果知道商品名称,就可以知道该商品的价格,也就是说,不存在商品名称相同而价格不同的记录,则可以说,“价格”函数依赖于“商品名称”,即商品名称→价格。
又如,设有以下关系模式。
学生(学号,姓名,年龄,性别,专业)
学生关系中有唯一的标识号“学号”,每个学生有且只有一个专业,则学号决定专业的值,因此,“专业”函数依赖于“学号”,也就是:学号→专业。
对于选修关系:
选修(学号,课程号,成绩)
可以看出学号与课程号共同决定一个成绩,因此“成绩”函数依赖于属性组(学号,课程号),也就是:(学号,课程号)→成绩。
需要注意的是,函数依赖是语义范畴的概念,只能根据数据的语义来确定函数依赖。例如,“姓名→年龄”这个函数依赖只有在不允许有同名人的条件下才成立。
定义2 在关系模式R(U)中,对于U的子集X和Y,如果X→Y,但Y,则称X→Y是非平凡的函数依赖。若X→Y,但Y⊆X,则称X→Y是平凡的函数依赖。
例如,对于关系:
选修(学号,课程号,成绩)
存在非平凡函数依赖:(学号,课程号)→成绩。
存在平凡函数依赖:(学号,课程号)→学号;(学号,课程号)→课程号。
对于任一关系模式,平凡函数依赖都是必然成立的,它不反映新的语义,因此若不特别声明,一般总是讨论非平凡函数依赖。
定义3 在关系模式R(U)中,如果X→Y,并且对于X的任何一个真子集X',都有X'Y,则称Y完全函数依赖于X,记作X
Y。若X→Y,但Y不完全函数依赖于X,则称Y部分函数依赖于X,记作X
Y。
例如,对于关系:
选修(学号,课程号,成绩)
由于学号成绩,课程号
成绩,而(学号,课程号)→成绩,因此:

又如,对于本小节开始提到的关系:
学生(学号,姓名,性别,课程号,课程名称,成绩)
由于有:(学号,课程号)→姓名,同时有:学号→姓名,因此存在部分函数依赖:

定义4 在关系模式R(U)中,如果X→Y,Y→Z,且YX,Y
X,则称Z传递函数依赖于X。
注意,如果Y→X,即X←→Y,则Z直接函数依赖于X。
例如,设有关系模式:
学生信息(学号,姓名,所在系,系主任)
经分析有:学号→所在系,所在系→系主任,因此,“系主任”传递函数依赖于“学号”。
3. 范式和规范化
规范化理论用于改造关系模式,通过分解关系模式来消除其中不合适的数据依赖,以解决数据冗余、插入异常、删除异常等问题。
所谓规范化,就是用形式更为简洁、结构更加规范的关系模式取代原有关系的过程。
要设计一个好的关系,必须使关系满足一定的约束条件,这种约束条件已经形成规范,分成几个等级,一级比一级要求更严格。满足最低一级要求的关系称为属于第一范式(Normal Form,NF),在此基础上如果进一步满足某种约束条件,达到第二范式标准,则称该关系属于第二范式,依此类推,直到第五范式。显然,满足较高范式条件的关系必须满足较低范式的条件。一个较低的范式,可以通过关系的无损分解转换为若干个较高级范式的关系,这一过程称为关系的规范化。
(1)第一范式(1NF)
定义 如果一个关系模式R的所有属性都是不可分的基本数据项,则R属于1NF。
第一范式是对关系模式的最起码的要求。不满足第一范式的数据库模式不能称为关系数据库。例如,表1-4的“学生”关系满足第一范式。
表1-4 属于第一范式的“学生”

而表1-5中的“工资”表具有组合数据项,是非规范化的表,不属于第一范式。
表1-5 具有组合数据项的“工资”

表1-6中的“职工信息”表具有多值数据项,因此不是规范化的表,不属于第一范式。
表1-6 具有多值数据项的“职工信息”

将表1-5和表1-6规范化为满足第一范式,分别见表1-7和表1-8。
表1-7 属于第一范式的“工资”

表1-8 属于第一范式的“职工信息”

满足第一范式的关系模式不一定就是一个好的关系模式。例如,对于表1-4的“学生”关系,本小节开始时已经分析过,它存在数据冗余、插入异常、删除异常等问题。
(2)第二范式(2NF)
定义 若关系模式R是1NF,并且每个非主属性都完全函数依赖于R的码,则R属于2NF。
例如,对于学生关系:
学生(学号,姓名,性别,课程号,课程名称,成绩)
已知该关系的码是(学号,课程号),因此,学号、课程号是主属性,性别、课程名称、成绩是非主属性。该关系存在以下部分函数依赖。
学号→姓名 学号→性别 课程号→课程名称
也就是存在非主属性对码的部分函数依赖,因此该关系不是2NF。改进的方法是对该关系进行分解,生成若干关系,以消除部分函数依赖。实际上,这里就是把描述不同主题的内容分别用不同的关系来表示,形成以下3个关系:
学生基本信息(学号,姓名,性别) 主码为学号 课程(课程号,课程名称) 主码为课程号 选修(学号,课程号,成绩) 主码为(学号,课程号)
可以看出,在这3个关系中不存在部分函数依赖,因此问题得到了解决。
(3)第三范式(3NF)
定义 如果关系模式R是第二范式,且每个非主属性都不传递函数依赖于主码,则R属于3NF。
也可以说,如果关系R的每一个非主属性既不部分函数依赖于主码,也不传递函数依赖于主码,则R属于3NF。
例如,表1-9的关系Housing是学生住宿收费表,SID为学生编号,Sname为学生姓名,Building为楼的编号,Fee为每季度需支付的费用。假设一个学生只住在一个大楼里,一个大楼只有一种收费标准。
表1-9 “Housing”关系

可以看出,每个SID可以唯一确定一条记录,SID是关系Housing的主码。按照语义“一个学生只住在一个大楼里,一个大楼只有一种收费标准”可知,存在以下函数依赖。
SID→Building Building→Fee
即存在非主属性Fee对码SID的传递函数依赖,因此关系Housing不是第三范式。该关系存在插入异常、删除异常等问题。例如,如果SID为15的学生退学了,则不仅删除了该学生的信息,同时删除了D楼的收费信息,出现了删除异常。如果新建了一个大楼,还没有学生入住,则该新楼的信息也无法插入到表中,也就是出现了插入异常。同样,如果有400名学生住在同一个楼里,则该楼的信息就要重复400次,因此也存在数据冗余问题。
可以将关系Housing分解为表1-10和表1-11所示的两个关系。
表1-10 “Stu-Housing”关系

表1-11 “Housing-Fee”关系

可以看出,分解后的关系解决了以上的插入异常、删除异常、数据冗余的问题。
在对关系进行规范化的过程中,一般要将一个关系分解为若干个关系。实际上,规范化的本质是把表示不同主题的信息分解到不同的关系中,如果某个关系包含有两个或两个以上的主题,就应该将它分解为多个关系,使每个关系只包含一个主题,但是,在分解关系之后,关系数目增多,需要注意建立起关系之间的关联约束(参照完整性约束)。关系变得更加复杂,对关系的使用也会变得复杂,因此并不是分解得越细越好。一般来说,用户的目标是第三范式(3NF)数据库,因为在大多数情况下,这是进行规范化功能与易用程度的最好平衡点。在理论上和一些实际使用的数据库中,有比3NF更高的等级,如BCNF、4NF、5NF等,但其对数据库设计的关心已经超过了对功能的关心,本书只讨论到3NF。