具有无损性连接和保持函数依赖的3NF分解

---------------------------------------------------------------------------------------------------------

本文欢迎转载,转载请附上链接http://blog.csdn.net/iemyxie/article/details/41246163

---------------------------------------------------------------------------------------------------------

数据库范式对于数据库而言无疑是非常重要的(废话。。)本文主要内容为c++实现具有无损性连接和保持函数依赖的3NF分解。

上篇文章中已经对各个范式进行了介绍,在此不再赘述。

何为第三范式?

第三范式(3NF)要求一个数据库表中不包含已在其它表中已包含的非主关键字信息,即消除了传递依赖。

算法伪代码

输入:关系模式R及R上的函数依赖集F

输出:R的3NF无损连接和保持函数依赖的分解Result

步骤如下:

1.求F的极小覆盖Fm

右部极小化

左部极小化

规则个数极小化

2.求F的正则覆盖Fc

合并Fm中具有相同左部的FD的右部

3.Result初始置空

for(Fc中的每个FD X->Y) do

if(Result中存在关系模式Ri,使得Ri包含于XY)then

Result=Result-Ri ∪XY

else if(Result中不存在Ri使得XY包含于Ri)then

Result=Result∪XY

if(Result中的关系模式都不包含R中的任何码)then

将R中的一个码添加到Result中

R^‘=A | A∈R,A不在F中出现

if(R’非空) then

将R’添加到Result中

return Result

算法C++实现(算法主体来自于@DarkSword

#include<iostream>
#include<string>
#include<algorithm>
#include<vector>
#include<map>
#include<stdio.h>
using namespace std;

string R; //关系模式
vector< pair<string,string> > F; // 函数依赖集(FD)
vector<string>subset; //关系模式 R 的所有子集
char *temp; //求所有子集的辅助变量
vector<string>candidate_key; // 所有的候选键
vector<string>super_key; //所有的超键
bool _includes(string s1,string s2){ //判断 s2 的每个元素是否都存在于 s1
	 sort(s1.begin(),s1.end());
	 sort(s2.begin(),s2.end());
	 return includes(s1.begin(),s1.end(),s2.begin(),s2.end()); // includes函数是基于有序集合的,所以先排序
}
bool _equal(string s1,string s2){ //判断两个集合是否相同
	 sort(s1.begin(),s1.end());
	 sort(s2.begin(),s2.end());
	 s1.erase( unique(s1.begin(),s1.end()),s1.end()); //排序去重之后判断是否相等即可
	 s2.erase( unique(s2.begin(),s2.end()),s2.end());
	 return s1==s2;
}
string get_attribute_closure(const string &X, const vector< pair<string,string> > &F){ //返回属性集X的闭包
	   string ans(X); //初始化 ans
	   string temp;
	   bool *vis = new bool[F.size()];
	   fill(vis,vis+F.size(),0);
	   do{
			temp=ans;
			for(int i=0;i!=F.size();++i){
				if(!vis[i] && _includes(ans,F[i].first) ){
					 vis[i]=1;
					 ans += F[i].second;
				}
			}
	   }while(temp!=ans); // ans 无任何改变时终止循环

	   delete []vis;
	   vis=NULL;
	   //删掉重复的
	   sort(ans.begin(),ans.end());
	   ans.erase( unique(ans.begin(),ans.end()),ans.end() );
	   return ans;
}
void _all_subset(int pos,int cnt,int num){ // get_all_subset()的辅助函数
	 if(num<=0){
		 temp[cnt]='\0';
		 subset.push_back(temp);
		 return ;
	 }
	 temp[cnt]=R[pos];
	 _all_subset(pos+1,cnt+1,num-1);
	 _all_subset(pos+1,cnt,num-1);
}
void get_all_subset(const string &R){ //求关系模式R的所有子集,保存在subset中
	 subset.clear();
	 temp=NULL;
	 temp=new char[R.size()];
	 _all_subset(0,0,R.length());
	 delete []temp;
	 temp=NULL;
}
bool is_candidate_key(const string &s){ //判断 s 是否是候选键
	 for(int i=0;i!=candidate_key.size();++i)
		 if(_includes(s,candidate_key[i])) //如果s包含了已知的候选键,那么s就不是候选键
			 return false;
	 return true;
}

bool cmp_length(const string &s1,const string &s2){ //对 subset 以字符串长度排序
	 return s1.length()<s2.length();
}
void get_candidate_key(const string &R, const vector< pair<string,string> > &F){//求关系模式 R基于F的所有候选键
	 get_all_subset(R);
	 sort(subset.begin(),subset.end(),cmp_length);
	 candidate_key.clear();
	 super_key.clear();
	 for(int i=0;i!=subset.size();++i){
		 if( _includes( get_attribute_closure(subset[i],F), R) ){
			 super_key.push_back(subset[i]);
			 if(is_candidate_key(subset[i]))
				 candidate_key.push_back(subset[i]);
		 }
	 }
}

typedef vector<pair<string,string> > vpss;
vpss get_minimum_rely(const vpss &F){ //返回 F 的依赖集
	 vpss G(F);
	 //使 G 中每个 FD 的右边均为单属性
	 for(int i=0;i!=G.size();++i){
		 if(G[i].second.length()>1){
			 string f=G[i].first, s=G[i].second,temp;
			 G[i].second=s[0];
			 for(int j=1;j<s.length();++j){
				 temp=s[j];
				 G.push_back( make_pair(f,temp) );
			 }
		 }
	 }

	 int MAXN=0;
	 for(int i=0;i!=G.size();++i)
		 if(G[i].first.length()>MAXN)
			 MAXN=G[i].first.length();
	 bool *del=new bool[MAXN];

	 //在 G 的每个 FD 中消除左边冗余的属性
	 for(int i=0;i!=G.size();++i){
		 if(G[i].first.length()>1){
			 fill(del,del+G[i].first.length(),0);
			 for(int j=0;j!=G[i].first.length();++j){ //对于第i个FD,判断是否可消除first的第j个属性
				 string temp;
				 del[j]=1;
				 for(int k=0;k!=G[i].first.length();++k)
					 if(!del[k])
						 temp+=G[i].first[k];
				 if( ! _includes(get_attribute_closure(temp,G),G[i].second) ) //不可删除
					 del[j]=0;
			 }
			 string temp;
			 for(int j=0;j!=G[i].first.length();++j)
				 if(!del[j])
					temp+=G[i].first[j];
			 G[i].first=temp;
		 }
	 }
	 delete []del;
	 del=NULL;

	 //必须先去重
	 sort(G.begin(),G.end());
	 G.erase( unique(G.begin(),G.end()),G.end());

	 //在 G 中消除冗余的 FD
	 vpss ans;
	 for(int i=0;i!=G.size();++i){ //判断第i个 FD 是否冗余
		 vpss temp(G);
		 temp.erase(temp.begin()+i);
		 if( ! _includes(get_attribute_closure(G[i].first,temp),G[i].second) ) //第 i 个 FD 不是冗余
			 ans.push_back(G[i]);
	 }
	 return ans;
}

vector<string> split_to_3nf(const string &R, const vector< pair<string,string> > &F){
	 vector< pair<string,string> > FF = get_minimum_rely(F); //保存 F的最小依赖集到 FF
	 // 把左部相同的 FD 用合并性合并起来
	 map<string,string> mp;
	 for(int i=0;i!=FF.size();++i){
		 if(mp.find(FF[i].first) == mp.end() )
			 mp[ FF[i].first ] = FF[i].second;
		 else
			 mp[ FF[i].first ] += FF[i].second;
	 }

	 FF.resize(mp.size());
	 int id=0;
	 map<string,string>::iterator It;
	 for(It=mp.begin(); It != mp.end(); ++It){
		 FF[id].first=It->first;
		 FF[id++].second=It->second;
	 }
	 // 每个 FD x->y 去构成一个模式 xy
	 vector<string> P;
	 for(int i=0;i!=FF.size();++i)
		 P.push_back(FF[i].first+FF[i].second);

	 get_candidate_key(R,F); //得到 R 的候选键
	 //在构成的模式集中,如果每个模式都不包含 R 的候选键,那么把候选键作为一个模式放入到模式集中
	 //这样得到的模式集是关系模式 R 的一个分解,并且这个分解既是无损分解,有能保持 FD .
	 for(int i=0;i!=candidate_key.size();++i){
		 int flag=0;
		 for(int j=0;j!=P.size();++j){
			 if( _includes(P[j],candidate_key[i]) ){
				 flag=1;
				 break;
			 }
		 }
		 if(!flag)
			 P.push_back(candidate_key[i]);
	 }
	 sort(P.begin(),P.end());
	 P.erase( unique(P.begin(),P.end()),P.end());
	 return P;
}
void init(){ //初始化
	 R="";
	 F.clear();
}
void inputR(){   //输入关系模式 R
	 cout<<"请输入关系模式 R:"<<endl;
	 cin>>R;
}
void inputF(){  //输入函数依赖集 F
	 int n;
	 string temp;
	 cout<<"请输入函数依赖的数目:"<<endl;
	 cin>>n;
	 cout<<"请输入"<<n<<"个函数依赖:(输入形式为 a->b ab->c) "<<endl;
	 for(int i=0;i<n;++i){
		 pair<string,string>ps;
		 cin>>temp;
		 int j;
		 for(j=0;j!=temp.length();++j){ //读入 ps.first
			 if(temp[j]!='-'){
				 if(temp[j]=='>')
					break;
				 ps.first+=temp[j];
			 }
		 }
		 ps.second.assign(temp,j+1,string::npos); //读入 ps.second
		 F.push_back(ps); //读入ps
	 }
}
int main(){
		 freopen("in.txt","r",stdin);
		 init();
		 inputR();
		 inputF();
		 vector<string> ans=split_to_3nf(R,F);
		 cout<<"将关系模式 R 无损分解且保持依赖地分解成 3NF 模式集,如下:"<<endl;
		 for(int i=0;i!=ans.size();++i)
			 cout<<ans[i]<<endl;
	return 0;
}

PS:代码亲测无误,不要用VC6.0运行(VC6.0对STL的标准支持的不好,编译可能会有很多Warning,运行会出错),可以使用DEV运行。

测试数据:

input sample

abcde

2

a->b

c->d

abc

2

a->b

bc->a

abcdef

4

ab->ef

d->e

e->f

cd->ef

abcdef

2

ab->cd

bc->ef

abcdef

4

ab->ef

c->d

d->e

bc->de

output sample

ab

ace

cd

ab

abc

abcd

abe

de

ef

abcd

bcef

abc

abef

cd

de

-------------------------------------------------------------------------------------------------------------

本文欢迎转载,转载请附上链接http://blog.csdn.net/iemyxie/article/details/41246163

-------------------------------------------------------------------------------------------------------------

时间: 2024-10-05 08:40:10

具有无损性连接和保持函数依赖的3NF分解的相关文章

考研复试数据库(四)

第四部分 关系数据库模式设计 (一).考核内容 (1) 关系约束与关系模式.关系的规范化设计 (2) 函数依赖与公理体系 (3) 关系模式的分解与规范化 (二).考核要求 (1)了解关系约束与关系模式的表示:理解关系模式规范化设计的必要性. (2)理解函数依赖的定义和相应的概念. (3)了解函数依赖的公理体系. (4)了解关系模式分解的相关概念. (5)理解完全函数依赖.部分函数依赖和传递函数依赖. (6)理解第一范式.第二范式.第三范式和BCNF范式的定义:掌握关系模式规范化的方法:掌握关系模

数据库复习11——关系模式与范式

数据库复习 CH11 关系模式与范式 11.1 关系模式的设计 数据库模式(Schema)是数据库中全体数据的逻辑结构和特征的描述,关系型数据库的模式又叫关系模式,我所理解的关系模式就是数据库中表结构的定义以及多张表之间的逻辑联系 关系模式的设计就是根据一个具体的应用,把现实世界中的关系用表的形式来表示的逻辑设计过程,不规范的关系模式设计会带来以下的问题: 数据冗余 更新异常 插入异常 删除异常 举ppt中例子说明四种问题,如下表中描述了老师信息(一个老师一个地址,可以教多门课,一门课只有一名老

数据库系统概论的范式转换问题

一.一个关系模式的码的求解方法 求候选码的简单方法方法: (1)如果有属性不在函数依赖集中出现,那么它必须包含在候选码中:(2)如果有属性不在函数依赖集中任何函数依赖的右边出现,那么它必须包含在候选码中:(3)如果有属性只在函数依赖集的左边出现,则该属性一定包含在候选码中.(4)如果有属性或属性组能唯一标识元组,则它就是候选码: 之后对于根据上述方法剔除和加入的key进行组合判断,即将已经选出来的key和其余剩下的属性进行一一组合,然后判断即可 例题: (1)R<U,F>,U=(A,B,C,D

关系模式范式分解教程 3NF与BCNF口诀

https://blog.csdn.net/sumaliqinghua/article/details/86246762 [通俗易懂]关系模式范式分解教程 3NF与BCNF口诀!小白也能看懂原创置顶 沃兹基.硕德 最后发布于2019-01-10 18:26:14 阅读数 13082 收藏展开本来是为了复习数据库期末考试,结果找了一圈都没有发现比较好的解释,通过查阅资料和总结,为大家提供通俗易懂的解法,一听就会!并且配有速记口诀!介是你没有玩过的船新版本包含最小依赖集求法候选码求法 在模式分解之前

数据库 无损连接分解

无损连接分解的形式定义如下:设R是一个关系模式,F是R上的一个函数依赖(FD)集.R分解成数据库模式δ={R1,……,Rk}.如果对R中每一个满足F的关系r都有下式成立: 那么称分解δ相对于F是“无损连接分解”,否则称为“损失连接分解”.其中表示自然连接. 从上述形式定义中可知,若直接根据定义来判断某个分解是否具有无损连接性,那么就得“对R中每一个满足F的关系r”进行测试,看是否满足上面的等式,这显然不可操作,因为“对R中每一个满足F的关系r”进行测试就意味着“对R中所有满足F的关系r”进行测试

关系数据库设计基础(函数依赖、无损连接性、保持函数依赖、范式)(转)

联系(Relationship) 1:1联系:如果实体集E1中的每个实体最多只能和实体集E2中一个实体有联系,反之亦然,那么实体集E1对E2的联系成为一对一联系,记为1:1: 1:N联系:一对多,记为1:N: M:N联系:多对多联系,记为M:N. http://zh.wikipedia.org/wiki/%E5%85%B3%E7%B3%BB%E4%BB%A3%E6%95%B0_(%E6%95%B0%E6%8D%AE%E5%BA%93) 函数依赖(Function Dependency) 定义 设

模式分解---无损连接性的判断方法(转)

例:已知R<U, F>,U= { A, B, C, D, E },F={ AB→C , D→E, C→D},R的一个分解ρ= {R1(A,B,C), R2(C,D), R3(D,E)}.判定分解ρ是否为无损连接的分解. 解: (1) 构造初始表: ---------> 关系模式R<U, F>的一个分解     ρ = { R1<U1,F1>,R2<U2,F2> }. 如果U1∩U2 → U1-U2?F+ 或 U1∩U2 → U2-U1?F+,那么ρ具有

数据库复习(函数依赖)(转)

如果我们要设计关系型数据库的表模式,则很有可能会出现冗余,为了避免这种情况,我们需要一些规则,这些规则称为依赖. 函数依赖简单地说就是属性集A推导出属性集B,比如 给定这些规则之后,如果某个关系能够满足给定的函数依赖,则称关系R满足函数依赖F: 在下面我们会介绍一系列的范式以及分解算法: 函数依赖的分解合并规则 与 是等价的(可以互相转化的),第一个式子替换第二个式子称为合并规则,第二个式子替换第一个式子称为分解规则: 平凡函数依赖:如果A-->B,A是B的超集,则称此函数依赖为平凡的. 平凡依

转换成3NF的保持函数依赖的分解算法:

ρ={R1<U1,F1>,R2<U2,F2>,...,Rk<Uk,Fk>}是关系模式R<U,F>的一个分解,U={A1,A2,...,An},F={FD1,FD2,...,FDp},并设F是一个最小依赖集,记FDi为Xi→Alj,其步骤如下: ① 对R<U,F>的函数依赖集F进行极小化处理(处理后的结果仍记为F): ② 找出不在F中出现的属性,将这样的属性构成一个关系模式.把这些属性从U中去掉,剩余的属性仍记为U: ③ 若有X→A€ F,且XA=