范式与反范式
1.1 范式
当设计关系型数据库时,需要遵从不同的规范要求,设计出合理的关系型数据库,这些不同的规范要求被称为不同的范式(Normal Form),越高的范式数据库冗余越小。应用数据库范式可以带来许多好处,但是最主要的目的是为了消除重复数据,减少数据冗余,让数据库内的数据更好的组织,让磁盘空间得到更有效的利用。范式的缺点:范式使查询变的相当复杂,在查询时需要更多的连接,一些复合索引的列由于范式化的需要被分割到不同的表中,导致索引策略不佳。
1.1.1 什么是第一、二、三、BC范式?
所谓“第几范式”,是表示关系的某一种级别,所以经常称某一关系R为第几范式。目前关系型数据库有六种范式:第一范式(1NF)、第二范式(2NF)、第三范式(3NF)、巴斯-科德范式(BCNF)、第四范式(4NF)和第五范式(5NF,又称完美范式)。满足最低要求的范式是第一范式(1NF)。在第一范式的基础上进一步满足更多规范要求的称为第二范式(2NF),其余范式以次类推。一般说来,数据库只需满足第三范式(3NF)就行了。满足高等级的范式的先决条件是必须先满足低等级范式。
在关系数据库中,关系是通过表来表示的。在一个表中,每一行代表一个联系,而一个关系就是由许多的联系组成的集合。所以,在关系模型中,关系用来指代表,而元组用来指代行,属性就是表中的列。对于每一个属性,都存在一个允许取值的集合,称为该属性的域。
下面介绍范式中会用到的一些常用概念。
① 实体(Entity):就是实际应用中要用数据描述的事物,它是现实世界中客观存在并可以被区别的事物,一般是名词。比如“一个学生”、“一本书”、“一门课”等等。值得强调的是,这里所说的“事物”不仅仅是看得见摸得着的“东西”,它也可以是虚拟的,比如说“老师与学校的关系”。
② 数据项(Data Item):即字段(Fields)也可称为域、属性、列。数据项是数据的不可分割的最小单位。数据项可以是字母、数字或两者的组合。通过数据类型(逻辑的、数值的、字符的等)及数据长度来描述。数据项用来描述实体的某种属性。数据项包含数据项的名称、编号、别名、简述、数据项的长度、类型、数据项的取值范围等内容。教科书上解释为:“实体所具有的某一特性”,由此可见,属性一开始是个逻辑概念,比如说,“性别”是“人”的一个属性。在关系数据库中,属性又是个物理概念,属性可以看作是“表的一列”。
③ 数据元素(Data Element):数据元素是数据的基本单位。数据元素也称元素、行、元祖、记录(Record)。一个数据元素可以由若干个数据项组成。表中的一行就是一个元组。
④ 码:也称为键(Key),它是数据库系统中的基本概念。所谓码就是能唯一标识实体的属性,它是整个实体集的性质,而不是单个实体的性质。它包括超码、候选码和主码。
⑤ 超码:超码是一个或多个属性的集合,这些属性的组合可以在一个实体集中唯一地标识一个实体。如果K是一个超码,那么K的任意超集也是超码,也就是说如果K是超码,那么所有包含K的集合也是超码。
⑥ 候选码:在一个超码中,可能包含了无关紧要的属性,如果对于一些超码,他们的任意真子集都不能成为超码,那么这样的最小超码称为候选码。
⑦ 主码:从候选码中挑一个最少键的组合,它就叫主码(主键,Primary Key)。每个主码应该具有下列特征:1.唯一的。2.最小的(尽量选择最少键的组合)。3.非空。4.不可更新的(不能随时更改)。
⑧ 全码:如果一个码包含了所有的属性,这个码就是全码(All-key)。
⑨ 主属性:一个属性只要在任何一个候选码中出现过,这个属性就是主属性(Prime Attribute)。
⑩ 非主属性:与主属性相反,没有在任何候选码中出现过,这个属性就是非主属性(Nonprime Attribute)或非码属性(Non-key Attribute)。
? 外码:关系模式R中的一个属性或属性组X并非R的码,但X是另一个关系模式的码,则称X是R的外码,也称外键(Foreign Key)。例如,在SC(Sno,Cno,Grade)中,Sno不是码,但Sno是关系模式S(Sno,Sdept,Sage)的码,则Sno是关系模式SC的外码。主码与外码一起提供了表示关系间联系的手段。
? 依赖表(Dependent Table):也称为弱实体(Weak Entity)是需要用父表标识的子表。
? 关联表(Associative Table):是多对多关系中两个父表的子表。
? 函数依赖:函数依赖是指关系中一个或一组属性的值可以决定其它属性的值。函数依赖就像一个函数y=f(x)一样,x的值给定后,y的值也就唯一地确定了,写作X → Y。函数依赖不是指关系模式R的某个或某些关系满足的约束条件,而是指R的一切关系均要满足的约束条件。
? 完全函数依赖:在一个关系中,若某个非主属性数据项依赖于全部关键字称之为完全函数依赖。例如,在成绩表(学号,课程号,成绩)关系中,(学号,课程号)可以决定成绩,但是学号不能决定成绩,课程号也不能决定成绩,所以“(学号,课程号)→ 成绩”就是完全函数依赖。
? 传递函数依赖:指的是如果存在“A → B → C”的决定关系,则C传递函数依赖于A。
下表列出了各种范式:
四种范式之间存在如下关系:
学习了范式,为了巩固理解,接下来设计一个论坛的数据库,该数据库中需要存放如下信息:
(1)用户:用户名,EMAIL,主页,电话,联系地址
(2)帖子:发帖标题,发帖内容,回复标题,回复内容
第一次可以将数据库设计为仅仅存在一张表:
用户名 EMAIL 主页 电话 联系地址 发帖标题 发帖内容 回复标题 回复内容
这个数据库表符合第一范式,但是没有任何一组候选关键字能决定数据库表的整行,唯一的关键字段用户名也不能完全决定整个元组。所以,需要增加“发帖ID”、“回复ID”字段,即将表修改为:
用户名 EMAIL 主页 电话 联系地址 发帖ID 发帖标题 发帖内容 回复ID 回复标题 回复内容
这样数据表中的关键字(用户名,发帖ID,回复ID)能决定整行:
(用户名,发帖ID,回复ID)→(EMAIL,主页,电话,联系地址,发帖标题,发帖内容,回复标题,回复内容)
但是,这样的设计不符合第二范式,因为存在如下决定关系:
(用户名)→(EMAIL,主页,电话,联系地址)
(发帖ID)→(发帖标题,发帖内容)
(回复ID)→(回复标题,回复内容)
即非关键字段部分函数依赖于候选关键字段,很明显,这个设计会导致大量的数据冗余和操作异常。
因此,需要对这张表进行分解,具体可以分解为(带下划线的为关键字):
(1)用户信息:用户名,EMAIL,主页,电话,联系地址
(2)帖子信息:发帖ID,标题,内容
(3)回复信息:回复ID,标题,内容
(4)发贴:用户名,发帖ID
(5)回复:发帖ID,回复ID
这样的设计是满足第1、2、3范式和BCNF范式要求的,但是这样的设计是不是最好的呢?不一定。
观察可知,第4项“发帖”中的“用户名”和“发帖ID”之间是1:N的关系,因此,可以把“发帖”合并到第2项的“帖子信息”中;第5项“回复”中的“发帖ID”和“回复ID”之间也是1:N的关系,因此,可以把“回复”合并到第3项的“回复信息”中。这样可以一定程度地减少数据冗余,新的设计如下所示:
(1)用户信息:用户名,EMAIL,主页,电话,联系地址
(2)帖子信息:用户名,发帖ID,标题,内容
(3)回复信息:发帖ID,回复ID,标题,内容
数据库表1显然满足所有范式的要求。
数据库表2中存在非关键字段“标题”、“内容”对关键字段“发帖ID”的部分函数依赖,满足第二范式的要求,但是这一设计并不会导致数据冗余和操作异常。
数据库表3中也存在非关键字段“标题”、“内容”对关键字段“回复ID”的部分函数依赖,也不满足第二范式的要求,但是与数据库表2相似,这一设计也不会导致数据冗余和操作异常。
由此可以看出,并不一定要强行满足范式的要求,对于1:N关系,当1的一边合并到N的那边后,N的那边就不再满足第二范式了,但是这种设计反而比较好。
对于M:N的关系,不能将M一边或N一边合并到另一边去,这样会导致不符合范式要求,同时导致操作异常和数据冗余。
对于1:1的关系,可以将左边的1或者右边的1合并到另一边去,设计导致不符合范式要求,但是并不会导致操作异常和数据冗余。
所以,满足范式要求的数据库设计是结构清晰的,同时可避免数据冗余和操作异常。这并意味着不符合范式要求的设计一定是错误的,在数据库表中存在1:1或1:N关系这种较特殊的情况下,合并导致的不符合范式要求反而是合理的。
所以,在数据库设计的时候,一定要时刻考虑范式的要求。
真题1、下列关于关系模型的术语中,所表达的概念与二维表中的“行”的概念最接近的术语是()
A、属性 B、关系 C、域 D、元组
答案:D。
二维表中的“行”即关系模型中的“元组”,二维表中的“列”即关系模型中的“属性”。
本题中,对于选项A,属性作为表中的列的概念。所以,选项A错误。
对于选项B,关系代表的是表和表之间的联系。所以,选项B错误。
对于选项C,域和选项A中的属性是一致的。所以,选项C错误。
对于选项D,二维表中的“行”即关系模型中的“元组”。所以,选项D正确。
所以,本题的答案为D。
真题2、在一个关系R中,如果每个数据项都是不可再分割的,那么R一定属于()
A、第一范式 B、第二范式 C、第三范式 D、第四范式
答案:A。
例如,帖子表中只能出现发帖人的ID,不能同时出现发帖人的ID与发帖人的姓名,否则,只要出现同一发帖人ID的所有记录,它们中的姓名部分都必须严格保持一致,这就是数据冗余。
本题中,在一个关系R中,若每个数据项都是不可再分割的,那么根据前面的解析应该属于第一范式,所以,选项A正确。
所以,本题的答案为A。
真题3、一个关系模式为Y(X1,X2,X3,X4),假定该关系存在着如下函数依赖:(X1,X2)→X3,X2→X4,则该关系属于()
A、第一范式 B、第二范式 C、第三范式 D、第四范式
答案:A。
对于本题而言,这个关系模式的候选键为{X1,X2},因为X2→X4,说明有非主属性X4部分依赖于候选键{X1,X2},所以,这个关系模式不为第二范式。
所以,本题的答案为A。
真题4、如果关系模式R所有属性的值域中每一个值都不可再分解,并且R中每一个非主属性完全函数依赖于R的某个候选键,则R属于()
A、第一范式(INF) B、第二范式(2NF) C、第三范式(3NF) D、BCNF范式
答案:B。
如果关系R中所有属性的值域都是单纯域,那么关系模式R是第一范式。符合第一范式的特点就有:(1)有主关键字;(2)主键不能为空;(3)主键不能重复;(4)字段不可以再分。如果关系模式R是第一范式的,而且关系中每一个非主属性不部分依赖于主键,则称关系模式R是第二范式的。很显然,本题中的关系模式R满足第二范式的定义。所以,选项B正确。
真题5、设有关系模式R(职工名,项目名,工资,部门名,部门经理)
如果规定,每个职工可参加多个项目,各领一份工资;每个项目只属于一个部门管理;每个部门只有一个经理。
(1)试写出关系模式R的基本函数依赖和主码。
(2)说明R不是2NF模式的理由,并把R分解成2NF。
(3)进而将R分解成3NF,并说明理由。
答案:(1)根据题意,可知有如下的函数依赖关系:
(职工名,项目名) → 工资
项目名 → 部门名
部门名 → 部门经理
所以,主键为(职工名,项目名)。
(2)根据(1),由于部门名、部门经理只是部分依赖于主键,所以该关系模式不是2NF。应该做如下分解:
R1(项目名,部门名,部门经理)
R2(职工名,项目名,工资)
以上两个关系模式都是2NF模式
(3)R2已经是3NF,但R1不是,因为部门经理传递依赖于项目名,故应该做如下分解:
R11(项目名,部门名)
R12(部门名,部门经理)
分解后形成的三个关系模式R11、R12、R2均是3NF模式。
1.1.2 反范式
不满足范式的模型,就是反范式模型。反范式跟范式所要求的正好相反,在反范式的设计模式中,可以允许适当的数据冗余,用这个冗余可以缩短取数据的时间。反范式其本质上就是用空间来换取时间,把数据冗余在多个表中,当查询时就可以减少或者是避免表之间的关联。反范式的优点:减少了连接,可以更好的利用索引筛选和排序,可以提高查询操作的性能;反范式的缺点:要在数据一致性与查询之间找到平衡点,符合业务场景的设计才是好设计。
在RDBMS模型设计过程中,常常使用范式来约束模型,但在NoSQL模型中则大量采用反范式。
数据库设计要严格遵守范式,这样设计出来的数据库,虽然思路很清晰,结构也很合理,但是,有时候却要在一定程度上打破范式设计。因为范式越高,设计出来的表可能越多,关系可能越复杂,但是性能却不一定会很好,因为表一多,就增加了关联性。特别是在高可用的OLTP数据库中,这一点表现得很明显。最明显的打破范式的设计方法就是冗余法,以空间换取时间的做法,把数据冗余在多个表中,当查询时可以减少或者是避免表之间的关联。
举例,有学生表与课程表,假定课程表要经常被查询,而且在查询中要显示学生的姓名,则查询语句为:
SELECT CODE,NAME,SUBJECT FROM COURSE C,STUDENT S WHERE S.ID=C.CODE WHERE CODE=?
如果这个语句被大范围、高频率执行,那么可能会因为表关联造成一定程度的影响,现在,假定评估到学生改名的需求是非常少的,那么,就可以把学生姓名冗余到课程表中。注意:这里并没有省略学生表,只不过是把学生姓名冗余在了课程表中,如果万一有很少的改名需求,只要保证在课程表中改名正确即可。
那么,修改以后的语句可以简化为:
SELECT CODE,NAME,SUBJECT FROM COURSE C WHERE CODE=?
范式和反范式的对比如下表所示:
模型 |
优点 |
缺点 |
范式化模型 |
数据没有冗余,更新容易 |
当表的数量比较多,查询设计需要很多关联模型(Join)时,会导致查询性能低下 |
反范式化模型 |
数据冗余将带来很好的读取性能(因为不需要Join很多表,而且通常反范式模型很少做更新操作) |
需要维护冗余数据,从目前NoSQL的发展可以看到,对磁盘空间的消耗是可以接受的 |
关于范式 Normal Form
范式是关系数据库理论的基础,也是我们在设计数据库结构过程中所要遵循的规则和指导方法。数据库的设计范式是数据库设计所需要满足的规范。只有理解数据库的设计范式,才能设计出高效率、优雅的数据库,否则可能会设计出错误的数据库。
目前关系数据库有六种范式:第一范式(1NF)、第二范式(2NF)、第三范式(3NF)、巴斯-科德范式(BCNF)、第四范式(4NF)和第五范式(5NF,还又称完美范式)。满足最低要求的叫第一范式,简称1NF。在第一范式基础上进一步满足一些要求的为第二范
式,简称2NF。其余依此类推。各种范式呈递次规范,越高的范式数据库冗余越小。通常所用到的只是前三个范式,即:第一范式(1NF),第二范式(2NF),第三范式(3NF)。
- 第一范式(1NF):强调的是列的原子性,即列不能够再分成其他几列。简而言之,第一范式就是无重复的列。
- 第二范式(2NF):首先要满足它是1NF,另外还需要包含两部分内容:一是表必须有一个主键;二是没有包含在主键中的列必须完全依赖于主键,而不能只依赖于主键的一部分。
即要求实体的属性完全依赖于主关键字。所谓完全依赖是指不能存在仅依赖主关键字一部分的属性。
- 第三范式(3NF):在1NF基础上,任何非主属性不依赖于其它非主属性[在2NF基础上消除传递依赖]。第三范式(3NF)是第二范式(2NF)的一个子集,即满足第三范式(3NF)必须满足第二范式(2NF)。
简而言之,第三范式(3NF)要求一个关系中不包含已在其它关系已包含的非主关键字信息。例如,存在一个部门信息表,其中每个部门有部门编号(dept_id)、部门名称、部门简介等信息。那么在员工信息表中列出部门编号后就不能再将部门名称、部门简介等与部门有关的信息再加入员工信息表中。如果不存在部门信息表,则根据第三范式(3NF)也应该构建它,否则就会有大量的数据冗余。简而言之,第三范式就是属性不依赖于其它非主属性,也就是在满足2NF的基础上,任何非主属性不得传递依赖于主属性。
关于范式的讨论
第二范式和第三范式如何区别?
第二范式:非主键列是否依赖主键(包括一列通过某一列间接依赖主键),要是有依赖关系的就是第二范式;
第三范式:非主键列是否是直接依赖主键,不能是那种通过传递关系的依赖的。要是符合这种就是第三范式;
使用范式有哪些优点和缺点?
范式可以避免数据冗余,减少数据库的空间,减轻维护数据完整性的麻烦。
范式再给我们带来的上面的好处时,同时也伴随着一些不好的地方:按照范式的规范设计出来的表,等级越高的范式设计出来的表越多。
如第一范式可能设计出来的表可能只有一张表而已,再按照第二范式去设计这张表时就可能出来两张或更多张表,如果再按第三范式或更高的范式去设计这张表会出现更多比第二范式多的表。
表的数量越多,当我们去查询一些数据,必然要去多表中去查询数据,这样查询的时间要比在一张表中查询中所用的时间要高很多。也就是说我们所用的范式越高,对数据操作的性能越低。
所以我们在利用范式设计表的时候,要根据具体的需求再去权衡是否使用更高范式去设计表。在一般的项目中,我们用的最多也就是第三范式,第三范式也就可以满足我们的项目需求,性能好而且方便管理数据;
当我们的业务所涉及的表非常多,经常会有多表发生关系,并且我们对表的操作要时间上要尽量的快,这时可以考虑我们使用“反范式”。
关于反范式
不满足范式的模型,就是反范式模型。
反范式跟范式所要求的正好相反,在反范式的设计模式,我们可以允许适当的数据的冗余,用这个冗余去取操作数据时间的缩短。本质上就是用空间来换取时间,把数据冗余在多个表中,当查询时可以减少或者是避免表之间的关联;
RDBMS模型设计过程中,常常使用范式约束我们的模型,但在NOSQL模型中则大量采用反范式。
反范式是通过增加冗余数据或数据分组来提高数据库读性能的过程。在某些情况下, 反范式有助于掩盖关系型数据库软件的低效。关系型的范式数据库即使做过优化, 也常常会带来沉重的访问负载。 数据库的范式设计会存储不同但相关的信息在不同的逻辑表, 如果这些表的存储在物理上也是分离的,那么从几个表中完成数据库的查询可能就会很慢 (比如JOIN操作)。如果JOIN操作的表很多,那么可能会慢得离谱。 有两个办法可以解决这个问题。首选的方法是使逻辑上的设计遵循范式, 但允许数据库管理系统(DBMS)在磁盘上存储额外的冗余信息来加快查询响应。 在这种情况下,DBMS还要保证冗余副本与原始数据的一致性。 这种方法通常在SQL中以索引视图(微软的SQL Server)或物化视图(Oracle)实现。 视图将信息表示为方便查询的格式,索引确保视图上的查询进行了优化。 更常见的做法是对数据做反范式设计。这种方法同样能提高查询响应速度, 但此时不再是DBMS而是数据库设计者去保证数据的一致性。 数据库设计者们通过在数据库中创建规则来保证数据的一致性,这些规则叫约束。 这样一来,数据库设计的逻辑复杂度就增加了,同时额外约束的复杂度也增加了, 这使该方法变得危险。此外,“约束”在加快读操作(SELECT)的同时,减慢了写操作 (INSERT, UPDATE和DELETE)。这意味着一个反范式设计的数据库, 可能比它的范式版本有着更差的写性能。 反范式数据模型与没有范式化的数据模型不同。 只有在范式化已经达到一定的满意水平并且所需的约束和规则都已经建立起来, 才进行反范式化。例如,所有的关系都属于第三范式, 连接的关系和多值依赖得到了妥善处理。
范式和反范式的对比
范式化模型 | 数据没有冗余,更新容易 |
当表的数量比较多, 查询设计需要很多关联模型(join)时,会导致查询性能低下 |
反范式化模型 |
数据冗余将带来很好的读取性能 (因为不需要join很多表,而且通常反范式模型很少做更新操作) |
需要维护冗余数据,从目前NoSQL的发展可以看到, 对磁盘空间的消耗是可以接受的 |
About Me
...............................................................................................................................● 本文作者:小麦苗,只专注于数据库的技术,更注重技术的运用
● 本文在itpub(http://blog.itpub.net/26736162)、博客园(http://www.cnblogs.com/lhrbest)和个人微信公众号(xiaomaimiaolhr)上有同步更新
● 本文itpub地址:http://blog.itpub.net/26736162/abstract/1/
● 本文博客园地址:http://www.cnblogs.com/lhrbest
● 本文pdf版及小麦苗云盘地址:http://blog.itpub.net/26736162/viewspace-1624453/
● 数据库笔试面试题库及解答:http://blog.itpub.net/26736162/viewspace-2134706/
● QQ群:230161599 微信群:私聊
● 联系我请加QQ好友(646634621),注明添加缘由
● 于 2017-07-01 09:00 ~ 2017-07-31 22:00 在魔都完成
● 文章内容来源于小麦苗的学习笔记,部分整理自网络,若有侵权或不当之处还请谅解
● 版权所有,欢迎分享本文,转载请保留出处
...............................................................................................................................
拿起手机使用微信客户端扫描下边的左边图片来关注小麦苗的微信公众号:xiaomaimiaolhr,扫描右边的二维码加入小麦苗的QQ群,学习最实用的数据库技术。