数据库-连接运算

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

数据库-连接运算
本博客会陆续写⼀些和操作数据有关的基本算法。

内容都很基础,算是帮助⼤家回顾记忆。

也可以给和我⼀样,刚接触数据库,数据挖掘等技术的同学,提供⼀个迅速了解基本算法的⽂档。

我认为多多体会基本算法,不光是为了编程、性能优化,还可以学习到很多分析解决问题的⽅法。

好了,不多废话,欢迎⼤家来评论;如⽂中有错误,也欢迎⼤家来拍砖哈~
我们查询数据时经常会⽤到联合查询
select r,s from R join S on R.id=S.rid
这个连接是怎么运算的呢?代价有多⼤呢?我们来分析⼀下。

先来看下运算结果集的⼤⼩:若R∩S=空,则连接为笛卡尔积。

若R∩S是R的码,可知s的⼀个元组⾄多与r的⼀个元组连接。

因此连接结果集的元组数不会超过s中的元组数。

若R∩S既不是R的码也不是S的码,令
R∩S={A},设S的元组数ns,V(A,s)为S中属性A所具有的不同值的数⽬,则ns/V(A,s)是S关系中属性A的值给定情况下平均的元组数⽬,则在r连接s的结果集中有nr*ns/V(A,s)个元组。

⾸先介绍最基础的连接⽅式:嵌套循环连接。

要计算r和s的theta连接,看伪代码:
for each 元组tr in r do
begin
for each 元组ts in s do
begin
测试元组对(tr,ts)是否满⾜连接条件theta
如果满⾜,把tr*ts加到结果中
end
end
若元组对数⽬是nr*ns,对于关系r中的每⼀条记录,我们必须对s做⼀次完整扫描。

最好的情况,内存空间容纳两个关系,此时每⼀数据块只需赌⼀次,从⽽只需bs+br次块存取(设bs,br代表含有关系S,R的元组块数⽬)。

如果较⼩的那个关系能完全放在内存中,把这个关系作为内层关系来处理,则内层循环关系只需读⼀次。

最坏的情况下,缓冲区只能容纳每个关系的⼀个数据块,这是共需nr*bs+br次块存取。

可以看到,当内存不能同时容纳两个关系,这种⽅法的代价很⾼。

下⾯我们做⼀下⼩的优化:
for each 块 Br of r do
begin
for each 块 Bs of s do
begin
for each 元组 tr in Br do
begin
for each 元组ts in s do
begin
测试元组对(tr,ts)是否满⾜连接条件θ
如果满⾜,把tr*ts加到结果中
end
end
end
end
此算法以块的⽅式⽽不是以元组的⽅式处理关系,叫做块嵌套循环连接。

最坏的情况下,对于外层关系的每⼀块,内层关系s的每⼀块只需读⼀次,需br*bs+br次块访问。

在块嵌套循环连接算法中,外层关系的块可以不⽤磁盘块作单位,⽽以内存中最多能容纳的⼤⼩为单位。

若内层循环连接属性上有索引,可以⽤更有效的索引查找法代替⽂件扫描法。

上⾯的两种算法可以⽤于所有连接运算:(如果是⾃然连接或等值连接中的连接属性是内层关系的码,则内层循环⼀旦找到⾸条匹配元组就可以终⽌。

)。

如果是⾃然连接或等值连接,还有更⾼效的算法:
归并连接
pr = r的第⼀个元组的地址;
ps = s的第⼀个元组的地址;
while(ps不等于null and pr不等于null) do
begin
ts=ps所指向的元组;
Ss={ts};
让ps指向关系s的下⼀个元组;
done=false;
while(not done and ps不等于null)do
begin
ts=ps所指向的元组;
if(ts[JoinAttrs]=tr[JoinAttrs])
then begin
Ss=Ss并ts;
让ps指向关系s的下⼀个元组;
end
else done=true;
end
tr=pr所指向的元组;
while(pr不等于null and tr[JoinAttrs]<ts[JoinAttrs])do
begin
让pr指向关系r的下⼀个元组;
tr=pr所指向的元组;
end
while(pr不等于null and tr[JoinAttrs]=ts[JoinAttrs])do
begin
for each ts in Ss do
begin
将ts连接tr加⼊结果中;
end
让pr指向关系r的下⼀个元组;
tr=pr所指向的元组;
end
end
归并连接算法为每个关系分配⼀个指针,这些指针⼀开始指向对应关系的第⼀个元组,随着算法的进⾏,指针遍历整个关系。

其中⼀个关系中在连接属性上有相同值的所有元组被加⼊到Ss中。

由于关系已排序,这样已排序的每⼀元组只需读⼀次,磁盘访问次数:br+bs。

散列连接
如果关系r的⼀个元组与关系s的⼀个元组满⾜连接条件,那么它们在连接属性上有相同的值。

若该值经散列函数映射为i,则关系r的那个元组必在Hri中,关系s的那个元组必在Hsi中。

因此,Hri中的元组只需与Hsi中的元组想⽐较,⽽⽆需与s的其它任何分划⽐较。

for each 元组 ts in s do
begin Hsi=h(ts[JoinAttrs]);
Hsi=Hsi并{ts};
end
for each 元组 tr in r do
begin Hri=h(tr[JoinAttrs]);
Hri=Hri并{tr};
end
for Hsi=0 to max do
begin 读Hsi在内存中建⽴其散列索引;
for each 元组tr in Hsi do
begin 检索Hsi的散列索引,定位所有满⾜tr[JoinAttrs]=ts
[JoinAttrs]的元组ts;
for each 匹配的元组ts in Hsi do
begin 将tr连接ts加⼊结果中;
end
end
end
选择的max值应⾜够⼤,以使对任意i,内存中可以容纳构造⽤输⼊关系的任意Hsi以及其上的散列索引。

最好⽤较⼩的输⼊关系作为构造⽤输⼊关系。

递归划分:如果max的值⼤于等于内存页帧数,关系的划分不可能⼀趟完成。

每⼀趟中,输⼊的可最多分裂的分化数不超过⽤于输出的缓冲页数。

每⼀趟产⽣的存储桶⼜在下⼀趟中分别读⼊并再次划分。

每趟划分中所⽤的散列函数与上⼀趟所⽤的散列函数是不同的。

分裂过程不断重复直到构造⽤输⼊关系的各个分划都能被内存容纳为⽌。

当M(内存块)>max+1,max=b/M,关系不需要递归划分。

溢出处理:当Hsi的散列索引⼤于内存时,构造⽤输⼊关系s的分划i发⽣散列表溢出。

原因:相同值元组多;散列函数没有随机性、均匀性。

分解:任给i,若发现Hsi太⼤,⽤另⼀个散列函数进⼀步将之划分。

避让:将关系s划分成许多⼩分划,然后把某些分划组合在⼀起,确保组合后的分划
能被内存容纳。

如果有⼤象相同值,不采⽤在内存中创建散列索引后⽤嵌套循环连接算法对分划连接的⽅法,⽽是在分划使⽤其他技术,如块嵌套循环连接。

代价:
⽆递归划分:3(br+bs)+2*max。

分划时读⼊写出2(br+bs),构造检索每个分划读⼊br+bs,max分划中每个分划可能有⼀个部分满的块,读写各⼀次,不超过2*max。

有递归划分:2(br+bs)[logM-1(bs)-1]+br+bs。

每⼀趟分划⼤⼩减⼩为原来的1/(M-1),直到每个分划最多占M块。

复杂连接
嵌套循环连接于块嵌套循环连接不管在什么条件下均可使⽤。

其他连接技术⽐嵌套循环连接更有效,但只能处理简单的连接条件,如⾃然连接或等值连接。

例如:
r与s在(theta1∩theta2∩…thetan)条件下的连接,可分别计算r与s在thetan下的连接,在计算其满⾜(theta1∩theta2∩…thetan)条件的连接。

3个关系的连接除了利⽤"结合律",还可以两边建索引,计算中间每⼀个元组与两边对应的元组。

相关文档
最新文档