当数据库运行一段时间之后我们才发现数据表设计有问题。重新调整数据表结构就需要做数据的迁移,还有可能影响程序的业务逻辑,以及网站的正常访问。
总之,开始设置数据库的时候我们需要重视数据表的设计。为了建立冗余较小、结构合理的数据库,设计数据库时必须遵循一定的规则。
在关系型数据库中,关于数据表设计的基本原则、规范就成为范式。可以理解为,一张数据表的设计结构需要满足的某种设计标准的级别。要想设计一个结构合理的关系型数据库,必须满足一定的范式。
范式的英文名称是Normal Form,简称NF。它是英国人在上个世纪70年代提出关系数据库模型后总结出来的。范式是关系数据库理论的基础,也是我们在设计数据库结构过程中所要遵循的规则和指导方法。
目前关系型数据库优六种常见范式,按照范式级别,从低到高分别是第一范式、第二范式、第三范式、巴斯-科德范式、第四范式和第五范式(又称完美范式)。
数据库的范式设计越高阶,冗余度就越低,同时高阶的范式一定符合低阶范式的要求,满足最低要求的范式是第一范式。在第一范式的基础上进一步满足更多规范要求的称为第二范式,其余以此类推。
一般来说,在关系型数据库设计中,最高也就遵循到巴斯范式,普遍是第三范式。但也不是绝对的,有时候为了提高某些查询性能,我们还需要破坏范式规则,也就是反范式化。
范式的定义会使用到主键和候选键,数据库中的键(key)由一个或多个属性组成。数据表中常用的几种键和属性的定义:
这里有两个表:
球员表(player):球员编号 | 姓名 | 身份证号 |年龄 |球队编号
球队表:(team):球队编号|主教练|球队所在地
第一范式主要是确保数据表中每个字段的值必须具有原子性,也就是说数据表中每个字段的值为不可再拆分的最小数据单元。
我们在设计某个字段的时候,对于字段x来说,不能把字段x拆分成字段x1和字段x2.事实上,任何dbms都会满足第一范式的要求,不会将字段进行拆分。
改变就不符合1NF,因为规则说表的每个属性必须是原子值,lisi和zhaoliu员工的emp_mobile值违反了改规则。为了使表符合1NF,我们应该用如下表:
有一个user表
其不符合第一范式。其中user_info字段为用户信息,可以进一步拆分成更小粒度字段。将user_info拆分后如下
属性的原子性是主管的。* 例如employees*表中雇员姓名应当使用1个(fullname)、2个(firstname,lastname)还是3个(fistname,middlename,和lastname)属性表示呢?答案取决于应用程序。如果程序需要分别处理雇员的姓名部分(如:用于搜索目的),则有必要把它们分开。否则,不需要。
第二范式要求在满足第一范式的基础上,还要满足数据表里的每一条记录都是可唯一标识的。而且所有非主键字段,都必须完全依赖于主键,不能之依赖主键部分。如果知道主键的所有属性的值,就可以检索到任何行的任何属性的任何值。(要求中的主键,可以拓展替换为候选键)。
成绩表(学号、课程号、成绩)关系中,(学号,课程号)可以决定成绩,但是学号不能决定成绩,课程号也不能决定成绩。所以(学号,课程号)–》成绩就是完全依赖关系。
比赛表player_game里包含球员编号、姓名、年龄、比赛编号、比赛时间和比赛场地等属性。这里候选键和主键都为(球员编号,比赛编号),我们可以通过主键来决定如下的关系:
(球员编号,比赛编号)-----》(姓名,年龄,比赛时间,场地,得分)
但是这个表不满足第二范式,因为表中字段还存在如下关系:
(球员编号)—》(姓名,年龄)
(比赛编号)—》(比赛时间,比赛场地)
对于非主属性来说,并非完全依赖候主键。这样会产生怎么样的问题呢?
这样的话,每张数据表都符合第二范式,也就避免了异常情况的发生。
1NF告诉我们字段属性需要是原子性,而2NF告诉我们一张表就是一个独立的对象,一张表只表达一个意思
定义了一个名为Orders的关系,表示订单和订单行的信息
违反了第二范式,因为有非主属性依赖于候选键(或主键)的一部分。例如,可以通过orderid找到订单的orderdate,以及customerid和companyname而没有必要再去使用productid
修改:
Orders表和OrderDetails表如下,此时符合第二范式。
小结:第二范式要求实体的属性完全依赖于主键字段。如果存在不完全依赖,那么这个属性和主键字段的一部分应该分离出来形成一个新的实体,新实体与原实体之间是一对多关系
第三范式是在第二范式的基础上,确保数据表中的每一个非主键字段都和主键字段直接相关,也就是说,要求数据表中的所有非主键字段不能依赖于其他非主键字段。即为不存在非主属性A依赖于非主属性B,非主属性B依赖于主键C的情况。通俗说,改规则的意思是所有非主属性之间不能有依赖关系,必须相互独立。
这里的主键可以拓展为候选键。
部门信息表:每个部门有部门编号、部门名称、部门简介等信息。
员工信息表:每个员工有员工编号、姓名、部门编号。列出部门编号就不能再将部门名称、部门简介等与部门有关的信息再加入员工信息表中。
如果不存在部门信息表,则根据3NF也应该构建它,否则就会又大量的数据冗余。
商品类别名称依赖于商品类别id不符合3NF
修改:
表1 符合3NF的商品类别表的设计
表2 符合3NF的商品表的设计
商品表goods通过商品类别id字段与商品类别表进行关联。
2NF和3NF通常以这句话概括“每个非键属性依赖于键,依赖于整个键,并且除了键别无他物”
关于数据表的设计,有三个范式要遵循。
范式本身没有优劣之分,只有适用场景不同。没有完美的设计,只有合适的设计,我们在数据表的设计中,还需要根据需求将范式和反范式混合使用。