定义:无损联接分解是将一个关系模式分解成若干个关系模式后,通过自然联接和投影等运算仍能还原到原来的关系模式,则称这种分解为无损联接分解。
无损分解的判定算法
输入:一个关系模式R(A1,A2,A3,...,An),R上的一个函数依赖集合F以及一个分解p{{R1,F1},{R2,F2},...,{Rk,Fk]}
输出:确定p是否是一个连接不失真分解
方法:
(1)建立建立用来检验连接是否失真的kxn表
A1 | A2 | ... | Aj | |
R1 | ||||
R2 | ||||
... | ||||
Rk |
(2)填表:若Aj属于Ri,则将第i行第j列填为aj,否则填入bij。
(3)修改表:逐一考察F中的函数依赖X->Y,X可能包含一个或者多个属性,如果这(个)些属性对应于表中的列的值相同,则值相等的行所对应的Y属性所有的列的值也相等。(比方说X->Y,X是A1A2所对应的属性,Y是属性A4。由第一步,如果表中第一行和第二行的值相等,那么表中A4对应的第一行和第二行的值也要修改成一样的。)如果其中有aj,则将bij改成aj;如果没有aj,就将他们都改成bij,一般来说i是值相等的行中最小的行号。
(4)反复(指在前一次修改地基础上,反复进行,直到表中的数据不再改变)进行(3),若发现某一行变成a1,a2,...,aj,则可以得出分解p{{R1,F1},{R2,F2},...,{Rk,Fk]}具有连接不失真性。
设R=ABCDE, R1=AD,R2=BC,R3=BE,R4=CDE, R5=AE, 设函数依赖:
A->C, B->C, C->D, DE->C, CE->A. 判断R分解成
ρ={R1, R2, R3, R4, R5}是否无损联接分解?
解:
这样的题要通过画表的方法来解,首先,原始表:
|
A
|
B
|
C
|
D
|
E
|
AD
|
a1
|
b
12
|
b
13
|
a4
|
b
15
|
BC
|
b
21
|
a2
|
a3
|
b
24
|
b
25
|
BE
|
b
31
|
a2
|
b
33
|
b
34
|
a5
|
CDE
|
b
41
|
b
42
|
a3
|
a4
|
a5
|
AE
|
a1
|
b
52
|
b
53
|
b
54
|
a5
|
表1
(A B C D E
是关系R
的属性, AD, BC, BE, CDE, AE
是分解之后每一个关系对应的属性集)
填表的过程:
当横竖相交的时候,如果在分解关系中存在对应列的单个的属性(譬如第一列第一行AD与A相交的单元格,AD含有A,就填写a1),则填写a
下标
, 下标就是单元格对应所在的列号。否则填写b下标, 下标是单元格对应所在的行列号。
填写之后的初始表就是表1
所示
2.根据依赖关系修改原始表:
对于依赖关系A->C,看A列中有两行a1是相等的(第一行和第五行),所以在C列中对应的两行也应该相等,但是看到这两行都是b(b13,b53),所以将这个b都换成b13(上面的较小的标)
|
A
|
B
|
C
|
D
|
E
|
AD
|
a1
|
b
12
|
b13
|
a4
|
b
15
|
BC
|
b
21
|
a2
|
a3
|
b
24
|
b
25
|
BE
|
b
31
|
a2
|
b
33
|
b
34
|
a5
|
CDE
|
b
41
|
b
42
|
a3
|
a4
|
a5
|
AE
|
a1
|
b
52
|
b53
à
b13
|
b
54
|
a5
|
对于依赖BàC, 同样的道理,看B这一列中,第二行和第三行都是a2,那么对C这一列同样的操作,但是看到C这一列中第二行是a3,那么就将第三行改成a3,优先级比b要高。
|
A
|
B
|
C
|
D
|
E
|
AD
|
a1
|
b
12
|
b
13
|
a4
|
b
15
|
BC
|
b
21
|
a2
|
a3
|
b
24
|
b
25
|
BE
|
b
31
|
a2
|
b33àa
3
|
b
34
|
a5
|
CDE
|
b
41
|
b
42
|
a3
|
a4
|
a5
|
AE
|
a1
|
b
52
|
b
13
|
b
54
|
a5
|
对依赖CàD,C列的1,5行相等,D的1,5行也应该相等,D的第1行有a,所以b54换成a4;另外C列的2,3,4行也相等,D的2,3,4行也应该相等,D的第4行有a,所以将对应的行都换成a4
|
A
|
B
|
C
|
D
|
E
|
AD
|
a1
|
b
12
|
b
13
|
a4
|
b
15
|
BC
|
b
21
|
a2
|
a3
|
b24àa
4
|
b
25
|
BE
|
b
31
|
a2
|
a3
|
b34àa
4
|
a5
|
CDE
|
b
41
|
b
42
|
a3
|
a4
|
a5
|
AE
|
a1
|
b
52
|
b
13
|
b54àa
4
|
a5
|
对于DEàC, DE公共的相等的行是3,4,5行,对应C的3,4,5行也应该相等,故将C列的两个的b13换成a3,所以表格经过这个函数依赖关系,就是:
|
A
|
B
|
C
|
D
|
E
|
AD
|
a1
|
b
12
|
b13àa
3
|
a4
|
b
15
|
BC
|
b
21
|
a2
|
a3
|
a4
|
b
25
|
BE
|
b
31
|
a2
|
a3
|
a4
|
a5
|
CDE
|
b
41
|
b
42
|
a3
|
a4
|
a5
|
AE
|
a1
|
b
52
|
b13àa
3
|
a4
|
a5
|
对于CEàA, CE的公共行是3,4,5行,所以将A的3,4,5行也对应相等,因为A列的第五行含有a1,所以将3,4行的b31,b41都换成a1
![](https://i-blog.csdnimg.cn/blog_migrate/06aa075f3cef3f53c1d5282544d26c00.jpeg)
最终得到的表格就是:
![](https://i-blog.csdnimg.cn/blog_migrate/0a94c9b35fbe945f89d238189c221d69.jpeg)
最后,我们从表格里看到对于DE行来说,都是a,所以得出结论,题中的分解是无损联接分解
********************
程序:
//数据库编程实验
//分解的无损连接判断
//输入:关系模式R(A1,A2,A3,...,An)
//R上的函数依赖集F以及R的一个分解 p={{R1,F1},{R2,F2},...,{Rk,Fk}}
//输出:用来检验连接是否失真的nxk表,以及是否连接无损的判断
#include <iostream>
#include <string>
//#include <stdlib.h>
using namespace std;
struct T
{
char word;//表中的标记字符a or b
int row;//行标
int col;//列标
};
struct FunctionDependence//函数依赖
{
string X;//决定因素
string Y;
};
void InitFD(FunctionDependence FD[],int n)
{
//函数依赖关系初始化
int i;
string x,y;
cout<<"请输入F中的函数依赖(决定因素在左,被决定因素在右)"<<endl;
//输入函数依赖集合F
for (i=0;i<n;i++)
{
cin>>x>>y;
FD[i].X=x;
FD[i].Y=y;
}
cout<<"函数依赖集合";
cout<<"F={" ;
for (i=0;i<n;i++)
{
//显示已知的函数依赖集合F
cout<<FD[i].X<<"->"<<FD[i].Y;
if (i<n-1)cout<<", ";
}
cout<<"}"<<endl;
}
bool T_equal(T t1,T t2)//比较两个表中的值是否完全相等
{
bool f=false;
if (t1.word==t2.word)
{
if (t1.row==t2.row)
{
if (t1.col==t1.col)
{
f=true;
}
}
}
return f;
}
bool IsIn(string f, string zz)
{
bool flag1 = false;
int len1 = f.length();
int len2 = zz.length();
int k = 0, t = 0, count1 = 0;
for (k = 0;k<len1;k++)
{
for (t = 0;t<len2;t++)
{
if (f[k] == zz[t])
{
//flag1=true;break;
count1++;
}
}
}
if (count1 == len1)
{
flag1 = true;
}
else flag1 = false;
return flag1;
}
bool CharIsIn(char f, string zz)
{
bool flag = false;
int len = zz.length();
int k = 0, count1 = 0;
for (k = 0;k<len;k++)
{
if (f == zz[k])
{
flag= true;;
}
}
return flag;
}
void show(string A,int lengthR)
{
for (int i=0;i<=lengthR;i++)
{
cout<<"--------";
}
cout << "\n\t";
for (int i = 0;i<lengthR;i++)
{
cout <<A[i] << "\t";
}
cout<<endl;
for (int i=0;i<=lengthR;i++)
{
cout<<"--------";
}
cout << endl;
}
int main()
{
//第零步:初始化
cout<<"请输入关系模式R(中间无间隔,无回车)"<<endl;
string A;
cin >> A;
int lengthR = A.length();
cout<<"关系模式R={";
for (int i = 0;i<lengthR;i++)
{
cout << A[i] ;
if (i<lengthR-1)cout<<",";
}
cout<<"}"<<endl;
int count = 0;
string R[lengthR+20];
cout<<"输入R的一个分解p(以0结束)\n";
while (cin >> R[count])//当输入的字符串不在R中时自动跳出
{
if (IsIn(R[count],A) == false) break;
count++;
}
cout<<"p={";
for (int i = 0;i<count;i++)
{
cout << R[i] ;
if (i<count-1)cout<<",";
}
cout<<"}";
int N;
cout<<"\n请输入F中函数依赖的组数:";
cin>>N;
FunctionDependence fd[N];
InitFD(fd,N);
//第一步:建立用来检验连接是否失真的nxk表,实际上应该是k行n列,不知道为什么还叫 nxk表
T table[count][lengthR];
cout << "\n构建初始表\n";
show(A,lengthR);
for (int k = 0;k<count;k++)
{
cout <<R[k] << "\t";
for (int j = 0;j<lengthR;j++)
{
table[k][j].col=j+1;
table[k][j].row=k+1;
if (CharIsIn(A[j], R[k]) == true)
{
table[k][j].word='a';
cout << table[k][j].word<<table[k][j].col<< "\t";
//行号不必显示,指定为一个值,用于整个列的判断
table[k][j].row=table[k][j].col ;
}
else
{
table[k][j].word='b';
//table[k][j].num=strcat(kk,jj);
cout << table[k][j].word<<table[k][j].row<<table[k][j].col<<"\t";
}
}
cout << endl;
}
for (int i=0;i<=lengthR;i++)
{
cout<<"--------";
}
cout<<endl;
//第二步:逐一考察F中的函数依赖,修改表
cout << "\n修改后的表\n";
show(A,lengthR);
bool flag1=false;
int left[8]={0},right,mini=1;//min表示最小的下标
int key=0;
for (int i=0;i<N;i++)
{
int l=fd[i].X.length();
for (int jj=0;jj<l;jj++)
{
for (int j = 0;j<lengthR;j++)
{
if (A[j]==(fd[i].X)[jj])
{
left[jj]=j;
}
//找到决定因素所在的属性列号
if (A[j]==(fd[i].Y)[jj])
{
right=j;
}
}
}
for(int k=0;k<count;k++)
{
for(int m=0;m<count;m++)
{
if (m==k){continue;}//避免自己和自己比较
for(int ll=0;ll<l;ll++)
{
if (T_equal(table[k][left[ll]],table[m][left[ll]])==true)
{
key++;
}
}
if (key==l)
{
//用来检验cout<<"("<<k+1<<"行,"<<*left+1<<"列)=("<<m+1<<"行,"<<*left+1<<"列)"<<endl;
if(table[k][right].word=='a')
{
//则修改被决定因素所在列的对应值
table[m][right].word='a';
table[m][right].col=right+1;//修改1
table[m][right].row=table[m][right].col;
//cout<<m+1<<"行,"<<right+1<<"列->a"<<right+1<<endl;
}
else if(table[k][right].word=='b')
{
if(table[m][right].word=='a')
{
table[k][right].word='a';
//行号不必显示,指定为一个值,用于整个列的判断
table[k][right].row=table[k][right].col ;
}
else if (table[m][right].word=='b')
{
if (table[m][right].row!=1)
{
mini=min(k,m);
if (mini>table[m][right].row)mini=table[m][right].row;
}else mini=0;
table[m][right].row=mini+1;//将其值改为整列最小的
table[m][right].col=right+1;
}
}
}
key=0;
}
}
}
//显示新的表的值
for (int i=0;i<count;i++)
{
cout <<R[i] << "\t";
for (int j=0;j<lengthR;j++)
{
if (table[i][j].word=='a')
{
cout<<table[i][j].word<<table[i][j].col<<"\t";
}
else cout<<table[i][j].word<<table[i][j].row<<table[i][j].col<<"\t";
}
cout<<endl;
}
for (int i=0;i<=lengthR;i++)
{
cout<<"--------";
}
//第三步:表的结构出现特征行,或者没有变化,从而判断连接是否无损
int temp=0;
bool flag=false;
for (int i=0;i<count;i++)
{
for (int j=0;j<lengthR;j++)
{
if (table[i][j].word=='a')
{
temp++;
}
}
if (temp==lengthR)
{
flag=true;break;
}
temp=0;
}
if (flag==true) cout<<"\n该分解具有无损连接性!"<<endl;
else cout<<"\n该分解不具有无损连接性!"<<endl;
return 0;
}
算法的第四步在本程序中没有体现到,需要外加一个循环,以及编写两个表是否相等的函数接口。本程序只是作为理解和检验的辅助。