数据库几大范式

来源:互联网 发布:js正则表达式函数 编辑:程序博客网 时间:2024/06/06 02:19


//文章有来自它处的引用,文章内容有任何不妥之处请留言相告,不胜感激!!!

  

  数据库的”范式“,指的是设计数据库的规则。按照一定的规则设计出数据库的表和关系,能够避免在一些情况下的查询出错,并具有良好的结构。总的来说,随着范式等级的提高,数据表属性之间的依赖关系越来越小,数据冗余越来越低。但同时,数据关系变得更加复杂,访问一个具体数据的关系层次增加。所以像设计模式一样,不应盲目追求范式等级,应根据具体需求来选择范式。

先来了解一下键的知识:

键(关系键)以及数据库范式都是关系数据库的概念。所谓关系键,指的是一个表中的一个(或一组)属性,用来标识该表的每一行与另一个表产生联系

键的定义:

超键(super key):在关系中能唯一标识元组的属性集称为关系模式的超键

候选键(candidate key):不含有多余属性的超键称为候选键

主键(primary key):用户选作元组标识的一个候选键程序主键

外键(foreign key)如果关系模式R1中的某属性集不是R1的主键,而是另一个关系R2的主键则该属性集是关系模式R1的外键。

结合实例的具体解释:

假设有如下两个表:

学生(学号,姓名,性别,身份证号,教师编号)

教师(教师编号,姓名,工资)

超键:由超键的定义可知,学生表中含有学号或者身份证号的任意组合都为此表的超键。如:(学号)、(学号,姓名)、(身份证号,性别)等。

候选键:候选键属于超键,它是最小的超键,就是说如果再去掉候选键中的任何一个属性它就不再是超键了。学生表中的候选键为:(学号)、(身份证号)。

主键:主键就是候选键里面的一个,是人为规定的,例如学生表中,我们通常会让“学号”做主键,教师表中让“教师编号”做主键。

外键:外键比较简单,学生表中的外键就是“教师编号”。外键主要是用来描述两个表的关系。

下面就来看一下常见的几种关系数据库范式吧。

一、第一范式(1NF)

  要求:

  •   每一个属性都不能再分割,都是原子项。

  第一范式是关系型数据表的基本要求,(1NF是对属性的原子性约束,要求属性具有原子性,不可再分解)

例如:

name       tel                                                   age

                手机                   座机

Josh        13612345678    021-9876543      22

Wang      13988776655    010-1234567      21

tel可以再分解为手机何电话,不具有原子性,不满足第一范式,做不出这样的表来。

修改方式:                                           

name               手机                   座机             age

Josh        13612345678    021-9876543      22

 

Wang      13988776655    010-1234567      21

这样分割之后,使得每一项都不能再分割,从而使得数据表满足第一范式。 

满足第一范式的数据表有什么好处呢?

  1. 1NF保证了数据库的每一列都是不同的。每一列的数据彼此没有任何交集。
  2. 这样做首先减少了数据的冗余,节省存储空间。如果不满足第一范式,一些数据项有可能包含相同的”子项“,造成存储空间的浪费。
  3. 其次,每一列没有重复的数据意味着不需要考虑数据更新的同步问题。不用担心在一列中更新了数据,还要在另一列做相应修改。
  4. 另外,每一列的数据不可再分,在某些情况下少了数据访问的层数,提高数据访问速度。

二、第二范式(2NF)就是在一个有唯一主键在表中保证每一行都具有唯一性。存在一个列被定义为唯一主键的表就是第二范式。

  要求:

  • 满足第一范式
  • 非主键属性均完全依赖于主键
  • 2NF是对记录的惟一性约束,要求记录有惟一标识,即实体的惟一性,更通俗说有主键ID

   非主键属性和主键可以有什么关系?1、完全依赖。2、部分依赖。3、不依赖(没关系)。显然第三种情况下,这个属性就不应该放在这张数据表中。所以2NF要求非主键属性完全依赖于主键,就是在消除非主键属性对主键的部分函数依赖。既然是部分函数依赖,暗含着说主键是一个复合键(由多个属性组成的键)。如果某个非主键属性只和主键中的一部分有关(部分函数依赖),则不符合第二范式。举例,网络游戏的用户数据表:

玩家用户名

角色名

角色职业

上次登录时间

 Alice

superman

wizard

2013-11-4 

  如果我们的游戏允许一个玩家拥有多个角色,则在这张表中“玩家用户名”和“角色名”构成复合主键,唯一标识一条记录。表中的“角色职业”,与玩家用户名和角色名均相关,为完全依赖于主键。而“上次登录时间”仅和“玩家用户名”相关,而与角色名无关。所以“上次登录时间”部分函数依赖于主键。本关系不符合2NF。

  要将上表转换为符合2NF的结构也很简单,只要把部分函数依赖的部分抽出来,组成新的表即可。如下所示:

玩家用户名

角色名

角色职业

Alice

superman

wizard

 

玩家用户名

上次登录时间

Alice

2013-11-4

  符合2NF能给我们带来什么好处呢?2NF消除了属性对主键的部分函数依赖。

  首先,2NF可以在一定程度上消除冗余,节省存储空间。

  如果存在部分函数依赖,则可能存在数据冗余。在多条记录中,主键中的某一个属性可能是一样的,而如果有其他数据项函数依赖于这个不变的属性,则这些数据项也将是一样的。比如在上面例子中,在修改之前的表中,如果有多个角色名对应一个玩家用户名,则会有多条数据。它们具有一样的用户名和不同的角色名。由于上次登录时间仅依赖于玩家用户名,所以在这多条记录中,上次登录时间也都是相同的,造成了冗余。

  其次,2NF简化了表的逻辑关系,使得表的结构更加清晰。

 

三、第三范式(3NF)就是主副(父子)两张表,在子表中在外键是父表中的主键,子表中的外键值必须是父表中的主键值。存在主外关系的两张表就是第三范式。

  要求:

  • 满足第一、二范式
  • 所有非主键属性之间没有函数依赖关系

  3NF在2NF的基础上,进一步消除非主键属性之间的函数依赖关系。实质上,也是消除非主键属性中的传递依赖。更进一步地说,如果两个数据表有关系。那么这两个数据表中的非主键属性必须是不同的。如果存在一个非主键属性A,存在于两张表中。则在某张表中,A依赖于外键,从而不符合3NF。比如网络游戏中拍卖行的数据,可以按照下面的表格进行存储:

玩家姓名

物品名

单价

数量

总金额

Alice

治疗药剂

50

10

500

  在这个表格中,“总金额”项可以通过“单价”和“数量”运算得出,存在函数依赖关系,不满足3NF。

  要将这个表格修改为满足3NF的要求,只需要从表中删除“总金额”即可。在另外一些情况中,可以将函数依赖关系涉及到的项单独抽出来组成新的表,需要具体情况具体分析。

  3NF的优点很明显,可以减少数据冗余,节省存储空间。既然存在函数依赖,某些数据项就能够通过其他数据项计算得出,很可能存在数据冗余。值得注意的是,在一些情况下,存在这种数据冗余的表格是有意义的。如果在表格中存储着某些运算的结果,我们在使用这些结果时就不用进行运算了,节省了运算时间,是一种“空间换时间”的做法。从这里也可以看出,应用范式并不能够保证最好的效果,需要根据应用需求进行合理取舍。

  

四、BC范式(所有属性均不传递依赖于任何候选键

  要求:

  • 满足1NF、2NF、3NF 
  • 所有属性(包含主键属性和非键主属性)都不传递依赖于任何候选键

  BC范式在3NF的基础上,要求主键属性也不能传递依赖于任何候选键。当主键是复合键是,主键的某个属性可能会依赖于某个候选键。此时,关系能够符合3NF,因为并不是“非主键”属性依赖于某个非主键属性。但此关系并不符合BC范式。例如,在以房间为组织方式的游戏中,我们记录某个玩家、房间和房主的关系。

房主ID

房间ID

玩家ID

Alice

123

Bob

  表中的依赖关系有:

  1. (玩家ID,房间ID)-> 房主ID
  2. 房主ID -> 房间ID
  3. (玩家ID,房主ID)-> 房间ID

  同时,表中的候选键有(玩家ID,房间ID)、(玩家ID,房主ID)。比如,我们选择主键为(玩家ID,房间ID),那么,房间ID就是主键的一个属性。而在依赖关系2中,房间ID依赖于房主ID,房主ID是候选键(玩家ID,房主ID)的一个属性。那么,首先,由于房间ID不是候选键属性,所以此表并没有违反3NF。但是由于房间ID和房主ID存在依赖关系,所以满足“主键属性传递依赖于某个候选键”的条件,所以此表不符合BC范式。

  要把上表修改为满足BC范式的形式,只要把它进行合理拆分即可。

房间ID

玩家ID

123

Bob

 

房间ID

房主ID

123

Alice

   BC范式的好处是进一步消除了表中的依赖关系,减少了冗余。例如在上例中,如果我们采用未修改的版本,如果想要存储一个10个玩家(不含房主)的房间,就需要10条这样的记录才可以。

  

五、第四范式(要求把同一表内的多对多关系删除)。

  要求:

  • 满足1NF、2NF、3NF
  • 表中不能包含一个实体的两个或多个多值属性

  所谓多值属性,指的是某个属性可以包含多个值。这个属性的(多个)取值,被另一个属性决定。也就是说,一旦确定了某个属性,另一个属性的多个取值就一起确定了。第四范式在第三范式的基础上,消除多值依赖。所谓多值依赖,指的是一组值(多值属性)依赖于另一个属性。函数依赖是一对一的关系,多值依赖是一对多的关系。这个理解起来我感觉有点别扭,可能我的理解也有偏差,说出来和大家一起探讨一下。

  比如,我们要在数据库中保存玩家的角色技能信息,这里我们允许一个玩家具有多个角色,一个角色具有多个技能:

 

玩家ID

角色名

技能

Alice

superman

Fire ball

  首先,这个表只有一个候选键(玩家ID、角色名、技能)。所以肯定符合3NF。进一步观察一下,玩家ID是一个单值属性。角色名就是一个多值属性了,因为一个玩家ID可以对应多个角色名。角色名在表中看起来是一项,这是由于受制于具体数据库提供的功能。逻辑上,我们拿到一个玩家ID,可以确定的是,这个玩家具有某些角色,是一个一对多的关系,是多值依赖。角色名是一个多值属性。同样的,一个角色也对应着多个技能,这也是多值依赖。技能也是一个多值属性。显然,这个表并不符合4NF。

  这个表有什么问题呢?

  首先,数据冗余大,如果一个玩家有好几个具有Fire Ball技能的角色,这个技能项就要重复保存几次。

  其次,增、删、改都比较复杂,比如我们要删除Fire Ball技能,那么,我们要删除这个玩家所有具有Fire Ball技能的表项。

  要将上表修改为符合4NF的表,只需要将多值依赖进行合理映射即可:

玩家ID

角色名

Alice

superman

 

角色名

技能

superman

Fire ball

  这两个表都符合4NF。

  可以看出,4NF的使用可以降低数据冗余,并且减少数据处理复杂度

  

六、第五范式(将表拆分为二元关系,一定会损失信息)

要求:

  • 满足1NF、2NF、3NF、4NF
  • 如果将表中的多元关系分解一个一个的二元关系,一定会丢失信息

  第五范式在4NF的基础上,进一步消除依赖。第五范式的要求明,如果不用这个表就不能正确说明数据之间的联系。所以符合5NF的表已经没有任何多余依赖的存在了。所以第五范式是一个比较理想的范式。比如我们存储玩家对战和其发生地点:

玩家1

玩家2

对战地点

Alice

Lisa

竞技场1

Alice

Bob

竞技场2

Bob

Lisa

竞技场1

  现在,我们把它拆分成三个二元关系:

玩家1

玩家2

Alice

Lisa

Alice

Bob

Bob

Lisa

 

玩家1

对战地点

Alice

竞技场1

Alice

竞技场2

Bob

竞技场1

 

玩家2

对战地点

Lisa

竞技场1

Bob

竞技场2

Lisa

竞技场1

 

  单独看这三个子表,我们可以得出以下结论:

  1. Alice和Bob对战过
  2. Alice在竞技场1和竞技场2都进行过对战
  3. Bob在竞技场1和竞技场2都进行过对战(结合第二、三个表)

 

  从这三个独立的结论,我们无法得知Alice和Bob究竟在那个竞技场进行的对战,也就是发生了信息丢失。所以上边那个表是符合5NF的。

 

原创粉丝点击