逻辑数据库设计阶段,负责把概念数据库模式变为逻辑数据库模式。说白了就是把之前编写的那张ER图转化为具体的数据库和数据表设计,我们要考虑主键外键,一个表有哪些字段,插入删除会不会出bug等等问题。但是,逻辑数据库不是看着那张图就凭空想出来的(虽然之前我一直是这么干的),它有一系列的数学规则可以遵循。除此之外,数学家们还规定了一系列数据库设计的范式,我们要按着数学规则,规范化我们的逻辑数据库,以达到符合设计范式的要求。
那么具体怎么操作呢?首先我们要有一个概念数据库设计,通常有一张ER图。接下来,我们按照简单的规则,形成一个初始的逻辑数据库设计,这个规则无非就是1:1怎么设计数据表,1:n怎么设计数据表等等,我认为不需要介绍了。
接下来,要确定每个初始关系的函数依赖集,然后使用关系数据库设计理论,对初始的关系模式进行规范化处理。这里面一下子出现了好几个没见过的词,本篇笔记主要记录支撑这些算法的基本概念。
t_student(name, age, grade)
就是一个关系模式。一个设计不好的关系模式可能存在一些问题,下面我们举个例子来介绍。
有一个关系模式R, R = {student_id, department_id, master_name, course_name, grade}
,字段分别是学生ID,系ID,系主任名,课程名,成绩。乍一看这个表设计的不知所云,确实就是很垃圾,但是为了说明问题,就举了这么一个例子。
上面具的这个例子,4个问题全占了,这不是一个好的关系设计。我们应该让问题尽可能少,因此我们有一些数学方法,能够把一个不好的关系模式分解,使其符合设计的规范。
学习如何分解前,需要预先了解函数依赖的概念。
函数依赖的定义具体表达式我就不写了,很绕。这是通俗的理解:一个关系模式中,X->Y
读作X属性函数地确定Y,或Y函数依赖于X。比如身份证号->年龄
表示假设在一个用户表里,身份证号相同的数据行,其年龄属性也相同。这不废话么,身份证号相同那就是一个人。
但是这里要注意,身份证号相同那就是一个人
,这是一个常识。也就是说,我们这里讨论的数据关系的函数依赖,只能通过常识确定。其次要注意,我们现在讨论的是同一个属性集中的两个属性。
X->Y
中,Y不是X的子集,就是非平凡依赖,我们只讨论非平凡依赖。废话,Y是X的子集还有啥可研究的,忘了这个名词吧。X->Y
中,X叫决定属性集。很好理解,身份证号->年龄
中,我们通过常识,用身份证号区分两个数据是不是同一个人的,身份证号就是决定属性集。X,Y,Z都是关系R的属性集:
X->Y
成立,对任意X的真子集Z,Z->Y
都不成立,则Y完全函数依赖与X。Y->X
不成立,X->Y
成立,Y->Z
成立,称Z传递函数依赖X。我们看一个传递函数依赖的例子:
R = {student_id, department_id, master_name}
根据常识,student_id->department_id
,以及department_id->master_name
。所以master_name
传递函数依赖student_id
。这是符合常识的,对任意一个学生只属于某一个系,也就间接和某一个系的唯一系主任有传递函数依赖关系。
关系模式规范化的处理过程中,需要由给定的函数依赖集合推导出所有函数依赖集合。这需要一个公理系统,这就是ArmStrong公理系统。这个公理系统是关系模式分解算法的基础。
X->Y
都成立,称F逻辑的蕴含X->Y
。ArmStrong公理系统的三条规则:
我们现在有:关系模式R
R的属性集合U
U的子集X,Y,Z
R的函数依赖集合F
X->Y
X->Y
,且Z⊆U,则F蕴含XZ->YZ
X->Y
,F蕴含Y->Z
,则F蕴含X->Z
证明过程就不写了,其实举个例子想一想也就是那么回事,只不过是用数学符号抽象的表达出来可读性比较差而已。
推论:
X->Y
,X->Z
,则X->YZ
成立X->Y
,WY->Z
,有XW->Z
成立X->Y
,且Z⊆Y
,有X->Z
成立引理:
X->A1A2...Ak
成立的充要条件是X->Ai
(i=1,2...k)我们现在有:关系模式R
R的属性集合U
R的函数依赖集合F
通俗的说,F+还是函数依赖的集合,里面有多个类似于X->Y
的东西,F+包括F自身,以及F中的函数依赖通过ArmStrong导出的所有函数依赖。既然这样,我们也就知道怎么求F+了,就是用上面一大段难懂的ArmStrong定理,推论,引理公式来求。别看公式复杂,实际上我们并不需要它们,我们之后会给出一个求解闭包的通用算法。
我们现在有 属性X,X⊆U
X->Ai
,X自身以及Ai的集合就是属性X关于F的闭包,记作X_F+。注意啊,所谓“X关于F的闭包”是属性集合,不是函数依赖集合,别和“F的闭包”弄混了。
下面看一个例题,顺便介绍一个求解“属性X关于F的闭包”问题的通用步骤。
问题:
U={A,B,C,D,E}
F={AB->C,AC->B,B->D,C->E,EC->B}
求(AB)+
算法步骤:
X={A,B}
逐一扫描F中各个函数依赖,找出左部为A,B或AB的函数依赖,我们得到 AB->C,B->D
X_new=X∪{C,D}={A,B,C,D}
X_new≠X,X=X_new
逐一扫描F中各个函数依赖,找出左部为{A,B,C,D}子集的函数依赖,得到AB->C,B->D,C->E,AC->B
X_new=X∪{B,C,D,E}={A,B,C,D,E}
X_new=U,算法终止,结论:(AB)_F+ = {A,B,C,D,E}
(AB)_F+ = {A,B,C,D,E}
同时意味着,AB是该关系的候选键,因此我们又得到了求一个关系的候选键的方法。
上面一段,我们给出了一个求解候选键的方法,其实也不能叫求解候选键,而是验证几个属性是不是候选键,因为一个关系模式的候选键也不是唯一的:
我们有 关系模式R
R的函数依赖集F
R的全部属性集合U
,设几个可能是候选键的属性,求其R上关于F的闭包,若结果为U,那么我们就可以说这几个属性是R的候选键。
下面介绍求解候选键的真正方法。
在此之前,我们还是不得不引入一堆概念
我们有 关系模式R
R的函数依赖集F
R的全部属性集合U
推论:
这个就是我们可以实际使用的最终结论了。我们可以用这个方法快速找出哪些属性组成的属性集最可能是候选键,最后通过“属性集关于F的闭包”进行验证。
满足下列条件的函数依赖集F称为“极小函数依赖集”:
X->A
,使得F和F-{X->A}
相等X->A
,X有真子集Z,使得F和(F-{X->A})∪{Z->A}
等价注意:极小函数依赖集不唯一。
求解极小函数依赖集F算法
遍历F中{X->Y},若Y=A1A2...Ak(K>=2),则使用{X->A1,X->A2...}替换Y
# 注:这一步作用实际是“使F中的任何一个函数依赖的右部仅含有一个属性”。
遍历F中{X->A},G=F-{X->A},若A∈X_G+,则从F中去掉{X->A}
遍历F中{X->A},设X=B1B2...Bm:
遍历Bi(i=1,2...m),若A∈(X-Bi)_F+,则用(X-Bi)->A取代X->A
手算计算量可是很大的啊!
现在我们再回顾一下介绍的函数依赖集相关概念。对于一个关系模式R,它有许多函数依赖X->Y
,这些函数依赖组成的集合就是函数依赖集。对于某一个函数依赖集F,我们能够求它的闭包F+,得到一个新的函数依赖集。对于一个R中的属性或属性集X,我们能够求X关于F的闭包X_F+,得到一个属性集。我们可以通过闭包得知某个属性集是否是一个关系的候选键,最后我们介绍了极小函数依赖集的概念。