关系规范化之分解的无损连接判定

定义:无损联接分解是将一个关系模式分解成若干个关系模式后,通过自然联接和投影等运算仍能还原到原来的关系模式,则称这种分解为无损联接分解。

无损分解的判定算法

输入:一个关系模式R(A1,A2,A3,...,An),R上的一个函数依赖集合F以及一个分解p{{R1,F1},{R2,F2},...,{Rk,Fk]}

输出:确定p是否是一个连接不失真分解

方法:

(1)建立建立用来检验连接是否失真的kxn表

 A1A2... Aj
R1    
R2    
...    
Rk    
第i行对应于Ri,第j列对应于属性Aj

(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]}具有连接不失真性。


例子: //这个例子出自 “ 李骥平” 博客 http://fsjoy.blog.51cto.com/318484/137130

设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列中对应的两行也应该相等,但是看到这两行都是bb13b53),所以将这个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公共的相等的行是34,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的公共行是345行,所以将A345行也对应相等,因为A列的第五行含有a1,所以将34行的b31,b41都换成a1
 
最终得到的表格就是:
 
 最后,我们从表格里看到对于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;
}
算法的第四步在本程序中没有体现到,需要外加一个循环,以及编写两个表是否相等的函数接口。本程序只是作为理解和检验的辅助。


  • 6
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值