概述:
经过一学期的数据库系统原理的学习,接触到了数据库范式。本文将主要讲解数据库的第一 第二 第三范式与BC范式之间的区别与联系,谈谈个人的见解。
什么是范式?
简单的说,范式是为了消除重复数据减少冗余数据,从而让数据库内的数据更好的组织,让磁盘空间得到更有效利用的一种标准化标准,满足高等级的范式的先决条件是满足低等级范式。(比如满足2nf一定满足1nf)
我们为什么需要范式?
关系数据库的设计主要是关系模式的设计,关系模式设计的好坏将直接影响到数据库设计的性能。将关系模式规范化是至关重要的。
接下来我将从第一范式到BC范式,逐个讲起每个范式的意义及其作用以及他避免的问题。
第一范式
第一范式是关系模式要遵循的基本范式。其定义为:
在关系模式R中,其所有属性均为简单属性,即每个属性都是不可再分的,那么则称R为第一范式
第一范式的定义十分容易理解,也十分容易做到。当我们要求关系模式为第一范式时,只需要在定义属性时做到简单属性即可。反之而言,如果一个关系模式不是第一范式,那么其存在某个或者某些属性组是由几个简单属性组合而成,那么关系模式就会转变为多层次的混合结构。这将很大程度的增加关系操作的复杂性。所以在关系数据库中第一范式是必须要满足的。
然而第一范式只是关系模式范式中最基础的一个模式,其本身依旧存在许多致命的问题。如数据冗余,插入删除更新的异常。
举个例子:
假设关系模式R为:
(学生ID,姓名,课程号,课程名,所在系,系主任,成绩)
数据冗余
可以得出这个关系模式属于第一模式。然而这样的关系模式什么问题呢?比如从数据冗余角度来说,我们有理工大类学生A,选了数学与英语课。那么在关系模式中则会存有两个字段:
(A_Id,A,课程号1,数学,理工大类,AAA,1.0)
(A_ID, A,课程号2,英语,理工大类,AAA,1.0)
从这我们可以看出,除了课程的相关属性,其他属性完全一样,纯粹是再存入了一遍,这明显造成了数据冗余。
插入异常
那么现在来一个转学生B,转学生初来乍到还没有选课,但是学生信息必须先录入那该怎么办呢?我们会发现我们遭遇了这样的窘状:
(B_ID,B,NULL,NULL,理工大类,AAA,NULL)
当我们在录入课程号和课程名的时候,完全无法填写,那么我们就无法插入新的记录。造成了插入异常。
删除异常
那么现在假设我们的B同学终于选了一门语文课,录入数据库以后突然觉得自己更喜欢数学课,于是出现了下面这一幕:
B:老师老师!我要改课,我想把我的语文课退了改成数学课!
老师:好的等一下我先把你的语文课记录删去….OK,搞定,现在给你登入数学课的记录,额..你学号是多少?
B:啊?学号?我忘了!!(小B刚来学校,还没完全记住学号) 直接在数据库里找我的学号不就好了
老师:啊可是我刚刚删掉了你的记录…
我们发现,刚刚老师在删除小B选的语文课记录时,这个关系模式中同样包含了小B的姓名学号等其他信息,当我们删除这条记录以后,可能会给我们带来其他数据的损失,这就是删除异常。
更新异常
小A是一个很矫情的小婊砸,有一天他突然觉得自己的名字不好听,去民政局把自己的名字改成了Accepted.一下子高大上了有没有!
于是理所当然的小Accepted跑去了数据库老师那更新信息,然后数据库老师便找到了
(A_Id,学生A,课程号1,数学,理工大类,AAA)
这条字段,很潇洒的把记录更改为
(A_Id, Accpted,课程号1,数学,理工大类,AAA)
心想这下肯定不会出差错了吧。然而细心的我们其实早就意识到,关于小A的记录并非只有一条,老师虽然将这条记录更改了,但是数据库中还存在着另外一条记录
(A_Id,学生A,课程号1,数学,理工大类,AAA)
这便是更新异常
事实上,这三种异常的情况有很多种,我所用的例子是最常见的异常情况,借此帮助我们理解第一范式在进行增查删改时所会遇到的异常理解。
对于第一范式所遇到的问题,我们要将其修改为第二范式,但是首先我们在改变为第二范式前,我们必须先引入一种名为函数依赖的关系:
对于关系模式R<U>,U为属性全集,X,Y分别为U的子集,
对于R<U>的任意一个记录r来说,每个X只能确定一个唯一的Y。
记作X-->Y,称为X确定Y,或者Y依赖X。
这个关系十分像我们初中时学习的函数y=f(x),每个x确定一个y,当然也可以多个X对应一个Y,即是多对一的关系。
值得注意的是: 我们在本文中大多数情况的是非平凡的函数依赖关系,即X不包含Y。反之,X包含Y的函数依赖称为平凡的函数依赖。
完全依赖,部分依赖
还是以关系模式R为例,我们发现之所以处于第一范式的R会有如此多的异常问题,究极根本原因是其中的属性值存在各种各样的函数依赖关系。
对于关系模式R,我们容易得出他的码为(学生ID,课程号ID),那么也就是说对于任何一个给定的码的记录而言,我们都可以通过这些信息得到这条记录的其他属性值信息。但是细心的同学会发现,对于某些属性值,我们只需要码的子集便可得出:
比如对于关系模式R(学生ID,姓名,课程号,课程名,所在系,系主任,成绩)
我们发现当我们需要得知学生姓名时,只需要学生ID即可。
同理我们需要课程名的时候只需要课程ID即可,而不需要整个码。
如果说对于属性组X,X–>Y,并且X的子集X’也同样决定了Y,即X‘–>Y。我们将其称为Y对X的部分函数依赖,记作X-P->Y
反之,如果不存在X的子集X’可以决定Y,并且X–>Y,那么我们将其称之为完全函数依赖,记作:X-F->Y
第二范式
当我们搞明白了部分函数依赖于完全函数依赖时,就可以很轻松的明白什么是第二范式。所谓的第二范式,就是指对于关系R属于第一范式,同时R的每个非主属性完全依赖于码,那么我们将其称之为第二范式。
从刚刚的关系模式R中,我们可以得到:
学号ID-P->姓名 课程号ID-P->课程名 (学号ID,课程号ID)-F->成绩
其他不写了...
所以从函数依赖关系上,我们可以将原来的表分为
R0(学生号,学生名,所在系,系主任)
R1(学生号,课程号,成绩)
R2 (课程号,课程名)
现在看起来比一开始顺眼多了。虽然依旧存在问题_(:зゝ∠)_
第三模式
假设学生B觉得理工大类读的好枯燥,一怒之下转到了经管大类开始了新生活,于是我们将他的信息更改为
(B_ID,B,经管大类,AAA)
这里我们就瞬间发现问题啦,问题在于虽然小B的所在系更改了,但是从现实的角度考虑小B的系主任不应该是原来的系主任啊? 但是为什么这个问题我们会一开始没有发现呢?原来虽然所在系与系主任对于关系R0的码来说都是完全依赖,但是从现实因素考虑,所在系依旧可以决定了这个系主任 即所在系–>系主任,我们发现这其中
学生号-->所在系, 所在系-->系主任
也就是说对于非主属性Z而言 ,存在属性组Y,使得X–>Y,Y–>Z。我们将其称之为Z对X的传递依赖。
而第三模式就是在第二模式的基础上去除传递依赖。也就是说我们只要在原来的基础上如此修改:
R0(学生号,学生名,所在系)
R1(所在系,系主任)
R2(学生号,课程号,成绩)
R3 (课程号,课程名)
便已经修改为第三范式了。而第三范式也在现实层面的应用中最为广泛。
BC范式
BC范式是第三范式的加强版,他强调了关系模式R中所有的属性(包括主属性和非主属性)都完全依赖于码或候选键,并且不存在传递依赖的情况。
从定义上我们可以看出,如果说第二范式是消除了非主属性对主属性的部分依赖,第三范式消除了非主属性对主属性的传递依赖,那么BC范式则是对所有属性消除了对于主属性的传递依赖与部分依赖。换句话说,在第三范式的基础上,他消除了主属性之间的传递依赖与部分依赖。