关系数据库设计
码和函数依赖:
令r(R)是一个关系模式。R的子集K是r(R)的超码的条件是:在r(R)中的合法实例中,对于r的实例中的所有元组t1, t2总满足:若t1≠t2则,t1[K]≠t2[K]。也就是说,在关系r(R)中没有两条元组在属性集K上可能具有相同的值。
考虑关系模式r(R),令a是R的子集,且b是R的子集。
给定r(R)的一个实例,称这个实例满足函数依赖a→b的条件是:对实例中所有元组t1和t2,若t1[a]=t2[a],则t1[b]=t2[b]。
如果在r(R)的每个合法实例中都满足函数依赖a→b,则称函数依赖a→b在关系模式r(R)上是成立的。
有些函数依赖称为平凡的,因为它们在所有关系中都满足。一般地,如果b是a的子集,则形如a→b的函数依赖是平凡的。
函数依赖理论:
给定关系模式r(R)上的函数依赖集F,如果r(R)的每一个满足F的实例也满足 f ,则R上的函数依赖 f 被 r 上的函数依赖集F逻辑蕴含。
Armstrong公理:
- 自反律,若a为一属性集且b是a的子集,则a→b成立。
- 增补律,若a→b成立且c为一属性集,则ac→bc成立。
- 传递律,若a→b和b→c成立,则a→c成立。
简化Armstrong公理:
- 合并律,若a→b和a→c成立,则a→bc成立。
- 分解律,若a→bc成立,则a→b和a→c成立。
- 伪传递律,若a→b成立和bc→d成立,则ac→d成立。
F的闭包是被F逻辑蕴含的所有函数依赖的集合,记作F+。
F的闭包F+:
给定一个关系R的函数依赖集F,则F的闭包F+的算法过程如下:
F+=F repeat for each F+中的函数依赖 f 在f上应用增补律和自反律 将结果放到F+中 for each F+中的一对函数依赖f1和f2 如果f1和f2能用传递律结合起来 则将结果放到F+中 until F+不再发生变化
属性a的闭包a+:
如果a→b,则称b为属性a函数确定。
令a为一个属性集。则将函数依赖集F下被a函数确定的所有属性的集合称为F下a的闭包,记作a+。计算F下a的闭包a+的算法过程如下:
result=a repeat for each 函数依赖 b→c in F do begin if b 属于result then result=result∪c end until(result不变)
如何获取R的所有候选码?
对于一个关系R,对其属性集合进行任意组合,然后依次计算这些属性组合的闭包。如果某个属性组合的闭包包含所有属性,那么这个属性组合就是候选码。
属性闭包算法的多种用途:
- 为了判断a是否为超码,我们计算a+,检查a+是否包含R中的所有属性。
- 通过检查b是否的a+的子集,可以检查出函数依赖a→b是否成立。
- 另一种计算F的闭包F+的方法:对任意的a,a是R的子集,可以找出闭包a+,对任意的b,b是a+的子集,可以输出一个函数依赖a→b。
如果去除函数依赖中的一个属性不改变该函数依赖集的闭包,则称该属性是无关的。
无关属性的形式化定义如下:考虑函数依赖集F及F中的函数依赖a→b,
- 如果A是a的一个元素并且F逻辑蕴含(F - {a→b})∪{(a-A)→b},则属性A在a中是无关的。
- 如果A是b的一个元素并且函数依赖集(F-{a→b})∪{a→(b-A)}逻辑蕴含F,则属性A在b中是无关的。
无损分解:将关系模式R分解为R1和R2,如果R1∩R2是R1或R2的超码,则R上的分解就是无损的。
Boyce-Codd范式:
具有函数依赖集F的关系模式R属于BCNF的条件是,对F+中的所有形如a→b的函数依赖(其中a是R的子集,并且b是R的子集),下面至少有一项成立:
- a→b是平凡的函数依赖(即,b是a的子集)
- a是模式R的一个超码
一个数据库设计属于BCNF的条件是,构成该设计的关系模式集中的每个模式都属于BCNF。
设R为不属于BCNF的一个模式。则存在至少一个非平凡的函数依赖a→b,其中a不是R的超码。则需要在设计中用以下两个模式取代R:
- a∪b
- R - ( b - a )
判定方法(避免计算F+是因为F+一般都很大):
在某些情况下,判定一个关系是否属于BCNF可简化成:
- 为了检查非平凡的函数依赖a→b是否违反BCNF,计算a+,并且验证它是否包含R中所有属性,即验证它是否是R的超码。
- 检查关系模式R是否属于BCNF,仅需检查给定集合F中的函数依赖是否违反了BCNF就足够了,不用检查F+中的所有函数依赖。
需要注意的是,如果一个关系没有被分解,那么我们可以证明如果F中没有函数依赖违反BCNF,那么F+中也不会有函数依赖违反BCNF。但是当一个关系分解后,这个结论将不再成立。
所以为了检查R分解后的关系Ri是否属于BCNF,可以用如下判定:
- 对于Ri中属性的每个子集a,确保a+要么不包含Ri-a的任何属性,要么包含Ri的所有属性。
如果Ri上有某个属性集a违反了该条件,考虑如下的函数依赖,可以证明它出现在F+中:
a→(a+-a)∩Ri
分解方法:
给出一个关系模式R,若R不属于BCNF,则可用下面的算法将R分解成一组BCNF模式R1,R2,...,Rn:
result:={R}; done:=false; 计算F+; while (not done) do if(result中存在模式Ri不属于BCNF) then begin 令 a→b 为一个在Ri上成立的非平凡函数依赖,满足 a→Ri 不属于F+,并且a∩b=Ø; result:=(result-Ri)∪(Ri-b)∪(a,b) end else done:=true;
下面给出一个例子:
给定关系模式class(class_id, title, dept_name, creadits, sec_id, semester, year, building, room_number, capacity, time_slot_id)
给出函数依赖(a→a+的形式):
course_id→title, dept_name, credits
building, room_number→capacity
course_id, sec_id, semester, year→building, room, time_slot_id
易知,该模式的候选码为{course_id, sec_id, semester, year}。
- 分解class:
函数依赖course_id→title, dept_name, credits成立,但course_id不是超码。因此class不属于BCNF。所以将class替换成:
course(course_id, title, dept_name, credits)
course-1(course_id, sec_id, semester, year, building, room_number, capacity, time_slot_id)
course上唯一成立的非平凡函数依赖:course_id→title, dept_name, credits,因为course_id是course的超码,所以course属于BCNF。
- 分解class-1
函数依赖building, room_number→capacity在class上成立,但{building, room_number}不是class-1的超码,所以将class-1替换成:
classroom(building, room_number, capacity)
section(course_id, sec_id, semester, year, building, room_number, time_slot_id)
显而易见,classroom和section属于BCNF
需要注意的一点是,如果一个关系已经被分离过了,那么在原有的函数依赖集合F中找不到使该关系不满足BCNF的函数依赖并不意味着这个关系就是函数依赖,而应该遍历函数依赖集合的闭包F+,来寻找相关的函数依赖。