Heim  >  Artikel  >  Datenbank  >  数据库范式:掀起你的盖头来

数据库范式:掀起你的盖头来

WBOY
WBOYOriginal
2016-06-07 15:35:461005Durchsuche

【介绍】 凡是做过数据库设计的,只要不是大菜鸟,听到范式两个字,马上就会在脑海中浮现1NF,2NF,3NF,BCNF这些术语,但是若要大家详细的将这几个东东的专业定义描述出来。。。。。。呵呵,至少大部分人都会晕的,什么功能依赖、函数依赖,~!@#¥%……*

【介绍】
       凡是做过数据库设计的,只要不是大菜鸟,听到范式两个字,马上就会在脑海中浮现1NF,2NF,3NF,BCNF这些术语,但是若要大家详细的将这几个东东的专业定义描述出来。。。。。。呵呵,至少大部分人都会晕的,什么功能依赖、函数依赖,~!@#¥%……&*,my god!反正我是记不住:)

      但既然上了IT这条贼船,就无法避免和数据库设计打交道,丑媳妇总是要见公婆的:)既然无法避免,那就让我们勇敢的面对,揭开数据库范式的神秘面纱,看看到底是一个丑媳妇还是一个俊媳妇!!

【一句话范式】
       数据库范式本身的定义这里就不啰嗦了,大家随便上网搜索或者找本教材看看,保证随处可见,我在这里给大家用通俗的语言描述出来。

      在详细探讨之前还是啰嗦一句:千万要注意:范式只是在设计表的时候用的原则,数据库其它的设计例如索引、视图、触发器、存储过程等都和范式没有关系。

【第一范式1NF】
1NF在数据库设计中其实很简单:列不可再分
什么叫列不可再分呢?意思就是每一列只包含一个属性,所有属性的类型都是一样的。

      可能有人看到这里会说:晕,这还用说,谁设计的数据库表的列还可以再分呢?其实这句话只对了一部分,下面我们详细分析为什么叫只对了一部分。

      我们目前说的和用的数据库都是关系数据库,而关系数据库中是不可能设计一个列能够包含两个不同类型的属性的;但大千世界,无奇不有,除了关系数据库,还有其它的数据库,例如面向对象数据库、XML数据库,这种数据库就可以设计一个属性再包含多个子属,所以1NF在关系数据库中是天然满足的,而在其它类型数据库就不一定了。

      看了上面这段话,你是不是长吁一口气,心里踏实了,以为就可以从此不管1NF了呢?非也,数据库只做了第一层保证,但你还是可以设计不符合1NF的表来。下面我们给几个简单的例子,看看我们如何不遵守1NF。

例子1:Student表有一个属性name,类型为字符串。

      这个是最简单的例子,估计很多人都如此设计过,数据库也不会拒绝我们创建这样的表,但这个设计是不符合1NF的,为什么?很简单的,名字其实是“有名有姓”的,这其实就是两个属性。比如我们要查找姓“李”的同学有多少,或者查询同名(例如“狗剩”)的有哪些。这样的查询如果只有一个name列,呵呵,只能用like来查询了,这样不同的名字方式(例如中国人姓在前,美国人姓在后),like都需要修改,而且like还不能利用索引加快查询速度。

例子2:Custom表中有一个属性Address。

大家可以自己按照上面的例子自己分析一下,看看会有什么问题……..

=========== 我是分割线 =============

【第二范式2NF】
2NF可以如下描述:不存在属于部分主键的属性
什么情况下会出现属于部分主键的属性呢?这个没有通用的原则,我们只能举一个例子来说明。

      例如:成绩表Score主键是StudentID和CourseID,另外还有两个属性:一个是成绩Grade,一个是StudentName。这里的Grade就是完全由两个主键决定的(标准术语叫做“完全依赖”),而StudentName就只是StudentID的属性(标准术语叫做“部分依赖”),因此这个设计就不符合2NF。

      可能有人会问:那我就把StudentID作为主键可不可以?当然是可以的,但这样一来CourseID就又不符合2NF了,因为CourseID不是StudentID的属性。

说了半天了,都是说我们要遵守范式,但是不遵守范式究竟有什么问题呢?
简单的说就是两个问题:冗余(存储、操作)、异常(插不进、删多了)

(1)冗余
       一个学生选了很多课,这样StudentName就要重复很多次,因为重复很多次,所以哪天学生改名了,所有的StudentName记录都要更新。

(2)异常
插不进:刚来一个学生,还没有选课(假设所有的课都要选),那么他的StudentName就没有地方记录
删多了:假如学生毕业的,所有的课都学完了,那么所有的记录都删除,这样学生的名字最后也没有地方记录了。

【第三范式3NF】
3NF可以这样描述:属性不递归依赖于主键
       什么叫递归依赖呢?简单的就是"A → B → C",那么C就是递归依赖(所谓依赖,简单的讲就是“有关系”了)于A了。

注意:在数据库中,存在这样的关系要求A是主键、B和C不是主键。

下面还是给一个简单的例子:

      假定学生关系表为Student(学号, 姓名, 年龄, 所在学院, 学院地点, 学院电话),关键字为单一关键字"学号",因为存在如下决定关系:
  (学号) → (姓名, 年龄, 所在学院, 学院地点, 学院电话)
  这个数据库是符合2NF的,因为每个学生的这些信息都和学生本人是有关系的,也就是说由学号来决定,但是不符合3NF,因为存在如下关系:
  (学号) → (所在学院) → (学院地点, 学院电话)
  即存在非关键字段"学院地点"、"学院电话"对关键字段"学号"的递归依赖。
  它也会存在数据冗余、更新异常、插入异常和删除异常的情况,大家可以根据2NF样例中的分析方法分析。

【其它范式】
还有一堆其它范式:BCNF,甚至变态的4NF、5NF。

      但几乎所有的大侠、大虾、专家都说3NF就足够了,没有必要搞那么多了,所以我也就不想追究了,大家若有兴趣可以自己研究一下。
      

=========== 我是分割线 =============

【范式的两面:天使与魔鬼】
有人说:掌握了数据库范式你就是数据库设计高手了!!

      呵呵,成为高手看来也很简单的啊:)但此话对了一半,掌握数据库范式可以算一般高手,因为毕竟能够掌握范式并应用的人不算很多;但算不上高高手,为什么呢?因为范式不是万能的,也有自己的适应范围和局限性,如果到处不加原则和场合的使用,它很可能就是一个带给你梦魇的魔鬼!

首先我们看看数据库范式的目的是什么?范式的主要目的有三个:
  • 减少数据冗余
  • 优化表结构
  • 避免操作异常
       从以上三个目的我们可以看出,范式的主要作用是和数据存储有关的,它要解决的问题是数据存储的问题。为了解决这些问题,范式的最终动作其实就是将表拆分,将表拆成更多的表。

      但是我们在实际应用过程中,不可能把数据存起来就不管了,而是要存要取,否则存起来有什么意义呢:)既然要涉及到数据读取,那么数据读取的性能当然也是要重点关注的;另外,程序是人写的,对于人来说,表是否容易理解、是否容易使用,也是相当重要的。如果表的数量很多、关系太复杂,使用的时候很容易出错。下面我们看看范式在这两点上的限制。

范式的限制主要有如下几个:
  • 表数量增多会带来更多的连接查询,而连接查询的效率肯定比不上单表查询;
  • 表数量增多会导致表的关系复杂,SQL语句会更加难写,表的关系也会更加难理解;
       基于以上两个限制,数据库设计中还有一个概念叫做“反范式”,反范式很简单,从字面意思上理解即可,就是范式的反操作。下面是简单的对比:

------------范式------------------------------反范式----------------------
目的   解决存储问题                 解决性能问题和使用问题
动作   拆表                               合表
效果   存储优化                        性能优化
-------------------------------------------------------------------------------------

      看到这里,可能会有人问:那是不是没有范式就是性能最好的呢?非也,因为影响性能的除了连接查询外,还有一个关键因素就是“锁”,如果所有信息都在一张表里面,那么对这张表的所有操作之间的冲突会非常厉害,反而会导致性能急剧下降。

      因此,数据库设计最终应该是在“范式”和“反范式”之间进行权衡,找到最佳的平衡点,只有这样才是一个真正的数据库设计高手!!

      到此数据库范式的盖头已经被我们全部掀开,当然最后发现这既不是一个俊媳妇,也不是一个丑媳妇,只是一个可以和我们过设计日子的普普通通的媳妇而已:)


来源:http://blog.csdn.net/yunhua_lee/article/details/4030864

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

数据库范式是DBA必知必会的知识,但是很多半路出家的DBA确往往忽略了这部分知识。
eugene面试问应聘者时,爱问int(n)、varchar(n)这里N代表的含义,如果我面试应聘者,我肯定会问范式的相关问题。
为了不难住大家,下面我以一个game dba的角度给大家简单分享下我对范式的认识。

范式简介:
范式的英文名字叫 normal from,他是美国人E.F.Codd在上世纪70年代提出关系数据库模型后总结出来的,范式是关系数据库的理论基础。

要学习范式,我们先学习几个关于范式的术语,当然为了方便大家理解,这里只是列出了部分常用的术语,如果要完整深入的
学习,请查考相关数据库书籍。

实体:任何事物都是一个实体,对于游戏数据库来说,一个玩家是一个实体,一个游戏道具是一个实体。
属性:实体具有的某个特性,比如玩家的角色名,等级,性别等,通常属性可以看做表中的一列。
元组:表中的一行就是一个元组。
码:表中可以唯一确定一个行(元组)的某个属性或者属性组叫做码。这里的码一般就是数据库表中的唯一索引,一个表中可以有多个唯一索引,这些唯一索引统称候选码,如果从多个唯一索引中选一个出来作为主键,这个主键叫做主码。

有一些基础的术语知识后,我们来了解范式。
目前关系数据库有六种范式:第一范式、第二范式、第三范式、第四范式、第五范式、第六范式。
满足范式的要求是递增的,也就是要满足第二范式,必须先满足第一范式,要满足第三范式,必须先满足第二范式.类推…
由于第四范式、第五范式、第六范式总要是用于消除冗余数据,现在的存储空间已经比较充裕,并且适当冗余存储可以提高查询的效率,所以当前的数据库设计,一般满足前三个范式即可。

第一范式、无重复的列
这个范式是关系数据库的基本要求,如果不满足,那你的表就不是关系数据库。
要求:数据库中的每一列是具有原子性,不可以拆分的。

如果某个列变成了二叉树那样,那就不是关系数据库了。

第二范式、属性
要求:所有属性完全依耐于主码,消除非候选码对主码的部分函数依耐。
通俗理解:所有属性完全依耐于主键,消除非主属性对主键的部分函数依耐。
通过一个玩家和家族关系的表来说明吧。

CREATE TABLE `family` (
  `userid` varchar(34) NOT NULL DEFAULT '',
  `familyid` int(11) NOT NULL DEFAULT '0',
  `familyname` varchar(32) DEFAULT NULL,
  PRIMARY KEY (`userid`,`familyid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

userid 玩家id
familyid 家族id
familyname 家族名
现在familyname其实只应依耐familyid,对(userid,familyid)这个组合主键存在部分依耐关系。
这样的表会出现哪些问题呢?

1、系统创建一个家族,userid的值为null,可以插入吗? 不可以,插入异常了。
有人说:让userid default null,你试试看。还有人说:设定一个系统使用的userid,当然可以,但这不是我们讨论的内容。
2、当一个家族有很多人加入以后,这个家族的名字存储了很多次 数据冗余了
3、当一个家族有很多人加入以后,需要修改一下家族名字? 会修改多少条记录?更新异常了
4、当所有人退出家族后,想要保留家族的存在?可以吗? 不可以,删除异常了

如何解决这个问题?
一般这种分体通过拆表就可以了,将上面的一个表改成下面的两个表,这样问题就都解决了。

CREATE TABLE `family3` (
  `userid` varchar(34) NOT NULL,
  `familyid` int(11) DEFAULT NULL,
  PRIMARY KEY (`userid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

CREATE TABLE `family4` (
  `familyid` int(11) NOT NULL DEFAULT '0',
  `familyname` varchar(32) DEFAULT NULL,
  PRIMARY KEY (`familyid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

第三范式、属性
要求:不依耐于其他非组属性,消除传递依耐。
这个咱也用刚才那个表,稍微改一下,把组合主键改成了userid为主键。

CREATE TABLE `family` (
  `userid` varchar(34) NOT NULL DEFAULT '',
  `familyid` int(11) DEFAULT NULL,
  `familyname` varchar(32) DEFAULT NULL,
  PRIMARY KEY (`userid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

现在familyname其实只应依耐familyid,对userid这个主键存在传递依耐关系。
会出现的问题和范式二一样。解决方法也和范式二一样。

来源:http://isadba.com/?p=261


2nf意思是,父亲姓什么,儿子就姓什么
3nf的意思是,孙子的姓是跟父亲一样就行了,虽然孙子也跟爷爷的姓也是一样的,但这是由于父亲跟爷爷的姓一样带来的(2nf),而不需要专门说孙子的姓要跟爷爷一样;如果有人说孙子的姓要跟爷爷一样,这就相当于违反了3nf,因为在2nf中已经确定了这一点

来源:http://www.itpub.net/thread-1493555-3-1.html

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn