数据库系统概念——关系数据库设计
关系数据库设计
关系数据库设计的目标是生成一组关系模式,使我们存储信息时避免不必要的冗余,并且让我们可以方便地获取信息。
这是通过设计满足适当范式的模式来实现的。
要确定一个关系模式是否属于期望的范式,我们需要数据库所建模的真实企业的信息。
其中的一部分信息存在于设计良好的E-R图中,但是可能还需要关于该企业的额外信息。
好的关系设计的特点
之前看到,可以直接从E-R设计生成一组关系模式。
显然,生成的模式集的好(或差)首先取决于E-R设计的质量。
设计选择:更大的模式
假定用以下这个模式代替instructor模式和department模式
i n s t _ d e p t ( I D , n a m e , s a l a r y , d e p t _ n a m e , b u i l d i n g , b u d g e t ) inst\_dept(ID, name, salary, dept\_name, building, budget) inst_dept(ID,name,salary,dept_name,building,budget)
这表示在instructor和department对应关系上进行自然连接的结果。
上面的设计选择中,存在的问题:
重复存储预算数额,且要承担某些用户可能更新一条元组而不是所有元组中的预算数额,并因此产生不一致的风险。
无法直接表示关于一个系的信息(dept_name, building, budget),除非该系在学校中有至少一位教师。
意味着不能记录新成立的系的信息,直到该系录用了第一位教师为止。
将不得不创建一条building 和 budget 为空值的元组。
设计选择:更小的模式
我们需要写这样一条规则:
如果存在模式(dept_name, budget),则dept_name可作为主码。这条规则被定义为函数依赖。
d e p t n a m e − − − > b u d g e t dept_name--->budget deptname−−−>budget
由于dept_name不能是inst_dept的主码(因为一个系可能在模式 inst_dept上的关系中需要多条元组),因此预算数额可能会重复。
类似于这些观察及由它们导出的规则(在此称为函数依赖)让数据库设计者可发现一个模式应拆分或分解成两个或多个模式的情况。
对有大量属性和多个函数依赖的模式,为找到正确分解,要依靠后面介绍的规范方法。
并不是所有模式分解都是有益的。
考虑把
e m p l o y e e ( I D , n a m e , s t r e e t , c i t y , s a l a r y ) employee(ID, name, street, city, salary) employee(ID,name,street,city,salary)
分解为以下两个模式:
e m p l o y e e 1 ( I D , n a m e ) employee1(ID, name) employee1(ID,name)
e m p l o y e e 2 ( n a m e , s t r e e t , c i t y , s a l a r y ) employee2(name, street, city, salary) employee2(name,street,city,salary)
不足是企业可能有两名同名雇员
我们能指出某个街道,城市,工资属于一个名为x的人,但无法区分是多个x中的那个。
下图所示为这些元组、利用分解产生的模式所生成的元组,以及我们视图用自然连接重新生成原始元组所得到的结果。
如在图中看到的,那两个原始元组伴随着两个新的元组出现于结果中,这两个新的元组将属于这两个名为Kim的职员的数据值错误地混合在一起。
我们能指出某个街道,城市,工资属于一个名为Kim的人,但无法区分是多个Kim中的那个。
将这样的分解称为有损分解,反之,则称为无损分解。
原子域和第一范式
E-R模型允许实体集和联系集的属性有某些程度的子结构。
特别地,它允许多值属性和组合属性。
当从包含这些类型的属性的E-R设计创建表时,要消除这种子结构。
对于组合属性,让每个子属性本身成为一个属性。
对于多值属性,为多值集合中的每个项创建一条元组。
一个域是原子的,如果该域内的元素被认为是不可分的单元。
我们称一个关系模式R属于第一范式,如果R的所有属性的域都是原子的。
名字的集合是一个非原子值的例子。
例如,如果关系employee的模式包含一个属性children,它的域元素是名字的集合,该模式就不属于第一范式。
组合属性,比如包含子属性street、city、state和zip的属性 address,也具有非原子域。
假定整数是原子的,那么整数的集合是一个原子域;然而,所有整数集的集合是一个非原子域。
区别在于,我们一般不认为整数具有子部分,但是我们认为整数的集合具有子部分——构成该集合的那些整数。
认为每个整数是一列有序的数字,那么全部整数的域就是非原子的。
使用函数依赖进行分解
基于码和函数依赖的方法,判断一个关系模式是否该分解。
讨论关系数据库设计的算法时,需针对任意的关系及其模式讨论,而不只是讨论例子。
-
一般情况下,用希腊字母表示属性集。
用小写的罗马字母后面跟一个用一对圆括号括住的大写字母来指关系模式(例如,r ( R ))
用r( R )表示该模式是关系r的,R表示属性集。
不关心关系名字时常简化为R。
一个关系模式是一个属性集,但是并非所有属性集都是模式。
使用小写希腊字母时,指一个可能是模式也可能不是模式的属性集。
希望指明属性集一定为一个模式时,使用罗马字母。 -
属性集是一个超码时,用K表示它。
超码属于特殊的关系模式,使用术语“K是 r( R )的超码”。 -
对关系使用小写的名字。
-
一个关系在任意给定时间都有特定的值。
当明显在讨论一个实例时,可仅用关系的名字(例如 r )
码和函数依赖
一个数据库对现实世界中一组实体和联系建模。
数据上常存在各种约束(规则)。
一个关系的满足所有约束的实例,称为关系的合法实例。
在一个数据库的合法实例中,所有关系实例都是合法实例。
几种最常用的现实世界约束可以形式化地表示为码(超码、候选码以及主码),或者下面所定义的函数依赖。
鉴于超码是能够唯一标识整条元组的属性集,函数依赖让我们可以表达唯一标识某些属性的值的约束。
考虑一个关系模式r®,令α⊆R且β⊆R
-
给定r( R )的一个实例,说这个实例满足(satisfy)函数依赖 α–>β的条件是:
对实例中所有元组对t1和t2,若t1[α]=t2[α],则t1[β]=t2[β]。 -
如果在r( R )的每个合法实例中都满足函数依赖α–>β,则我们说该函数依赖在模式r®上成立。
有些函数依赖称为平凡的,因为无论对什么关系模式都会满足。
一般地,如β⊆α,则形如α–>β的函数依赖是平凡的。
将使用 F + F^{+} F+符号来表示F集合的闭包,也就是能够给定F集合推导出的所有函数依赖的集合。
显然, F + F^{+} F+包含F中所有的函数依赖。
Boyce-Codd 范式
Boyce-Codd(Boyce-Codd Normal Form, BCNF )范式:
它消除所有基于函数依赖能够发现的冗余,虽然,可能有其他类型的冗余还保留着。
具有函数依赖集F的关系模式R属于BCNF的条件是,对 F + F^{+} F+中所有形如α–>β的函数依赖(其中α⊆R且β⊆R),下面至少有一项成立:
- α–>β是平凡的函数依赖(即β⊆α)。
- α是模式R的一个超码。
一个数据库设计属于BCNF的条件是,构成该设计的关系模式集中的每个模式都属于BCNF。
分解不属于BCNF的模式的一般规则。
设R为不属于BCNF的一个模式。
则存在至少一个非平凡的函数依赖α–>β,其中α不是R的超码。
在设计中用以下两个模式取代R:
- (αUβ)
- (R-(β-α))
当分解不属于BCNF的模式时,产生的模式中可能有一个或多个不属于BCNF。
这种情况中,需要进一步分解,直到最终结果是一个BCNF模式集合
BCNF 和保持依赖
多种表达式数据库一致性约束的方式:主码约束、函数依赖、check约束、断言和触发器。
在有些情况下,到BCNF的分解会妨碍对某些函数依赖的高效检查。
由于我们的设计使得该函数依赖的强制实施在计算上很困难,因此我们称为我们的设计不是保持依赖的。
第三范式
具有函数依赖集F的关系模式R属于第三范式(third normal form)的条件是:
对 F + F^{+} F+中所有形如α–>β的函数依赖(其中α⊆R且β⊆R)以下至少一项成立:
- α–>β是一个平凡的函数依赖。
- α是R的一个超码。
- β - α 中的每个属性A都包含于R的一个候选码中。
注意上面的第三个条件并没有说单个候选码必须包含 β - α 中的每个属性A可能包含于不同的候选码中。
候选码——任何真子集都不是超码的超码。
3NF定义中的第三个条件看起来很不直观,并且它的用途也不是显而易见的。
在某种意义上,它代表BCNF条件的最小放宽,以确保每一个模式都有保持依赖的3NF分解。
注意任何满足BCNF的模式也满足3NF,因为它的每个函数依赖都将满足前两个条件中的一条。所以BCNF是比3NF更严格的范式。
3NF的定义允许某些BCNF中不允许的函数依赖。
只满足3NF定义中第三个条件的依赖 α→β 在BCNF中是不允许的,但在3NF中是允许的。
当不存在保持依赖的BCNF设计时,必须在 BCNF和 3NF 之间进行权衡。
更高的范式
某些情况下,使用函数依赖分解模式可能不足以避免不必要的信息重复。
多值属性 phone_number 和 child_name 中每个属性对应一个模式:
( I D , c h i l d n a m e ) (ID, child_name) (ID,childname)
( I D , p h o n e n u