大家在开发过程中应该都有如下感受:良好的表结构设计是高性能的基石,应该根据系统将要执行的业务查询来设计;糟糕的表结构设计,会浪费大量的开发时间,严重延误项目开发周期,让人痛苦万分,而且直接影响到数据库的性能,并需要花费大量不必要的优化时间,效果往往还不怎么样。在数据库表设计上有个很重要的设计准则:范式设计。这个是大家在学习数据库时的第一道门槛,但是在实际工作中很少有完全遵循3级范式进行设计的。
什么是范式?
要想设计—个好的关系,必须使关系满足一定的约束条件,此约束已经形成了规范,分成几个等级,一级比一级要求得严格。满足这些规范的数据库是简洁的、结构明晰的,同时,不会发生插入(insert)、删除(delete)和更新(update)操作异常。反之则是乱七八糟,不仅给数据库的编程人员制造麻烦,而且极难维护,可能存储了大量不需要的冗余信息。
目前关系数据库有六种范式:第一范式(1NF)、第二范式(2NF)、第三范式(3NF)、巴斯-科德范式(BCNF)、第四范式(4NF)和第五范式(5NF,又称完美范式)。满足最低要求的范式是第一范式(1NF)。在第一范式的基础上进一步满足更多规范要求的称为第二范式(2NF),其余范式以次类推。一般来说,数据库只需满足第三范式(3NF)就行了。
第一范式
属于第一范式关系的所有属性都不可再分,即数据项不可分(第一范式强调数据表的原子性,是其他范式的基础)。如下表所示:
id | name-sex |
---|---|
1 | 张三-男 |
name-sex列具有两个属性,一个name,一个sex不符合第一范式,把它拆分成两列
id | name | sex |
---|---|---|
1 | 张三 | 男 |
上表就符合第一范式关系。但工作中仅用第一范式来规范表格是远远不够的,依然会存在数据冗余过大、删除异常、插入异常、修改异常的问题,此时就需要引入规范化概念,将其转化为更标准化的表格,减少数据依赖。实际上,1NF是所有关系型数据库的最基本要求,在关系型数据库管理系统(RDBMS),例如SQL Server,Oracle,MySQL中创建数据表的时候,如果数据表的设计不符合这个最基本的要求,那么操作一定是不能成功的。也就是说,只要在RDBMS中已经存在的数据表,一定是符合1NF的。
第二范式
第二范式(2NF)是在第一范式(1NF)的基础上建立起来的,即满足第二范式(2NF)必须先满足第一范式(1NF)。
第二范式(2NF)要求数据库表中的每个实例或行必须可以被惟一地区分。简单来说,需要为表加上一个列,以存储各个实例的惟一标识。例如订单信息表中加上了订单编号(order_no)列,因为每个订单的订单编号是惟一的,因此每个订单可以被惟一区分。这个唯一属性列被称为主关键字或主键、主码。也就是说要求表中只具有一个业务主键,而且第二范式(2NF)要求实体的属性完全依赖于主关键字。所谓完全依赖是指不能存在仅依赖主关键字一部分的属性。如下表所示:
有两张表:订单表,商品表
订单表id | 下单时间 | 产品id |
---|---|---|
1 | 2023-12-11 11:53:26 | 2 |
1 | 2023-12-11 11:53:26 | 3 |
商品表id | 商品名称 |
---|---|
2 | 数据库权威指南 |
3 | 大话西游 |
一个订单有多个产品,所以订单的主键为【订单ID】和【商品ID】组成的联合主键,这样2个主键不符合第二范式,而且商品ID和订单ID没有强关联,所以需要把订单表进行拆分为订单表与订单与商品的中间表。如下表所示: | |
订单表id | 下单时间 |
– | – |
1 | 2023-12-11 11:53:26 |
订单-商品中间表id | 订单表id | 商品id |
---|---|---|
1 | 1 | 2 |
2 | 1 | 3 |
商品表id | 商品名称 |
---|---|
2 | 数据库权威指南 |
3 | 大话西游 |
第三范式
每一个非主属性既不部分依赖于也不传递依赖于业务主键,也就是在第二范式的基础上消除了非主键对主键的传递依赖。例如商品信息表,其中每个商品有商品编号(product_id)、商品名称、商品简介等信息。那么在订单商品中间信息表中列出商品编号后就不能再将商品名称、商品简介等与商品有关的信息再加入订单商品中间信息表中。如果不存在商品信息表,则根据第三范式(3NF)也应该构建它,否则就会有大量的数据冗余。
订单表id | 下单时间 | 商品id | 商品名称 |
---|---|---|---|
1 | 2023-12-11 11:53:26 | 2 | 数据库权威指南 |
1 | 2023-12-11 11:53:26 | 3 | 大话西游 |
商品表id | 商品名称 |
---|---|
2 | 数据库权威指南 |
3 | 大话西游 |
1、商品 ID与订单编号存在关联关系;
2、商品名称与订单编号存在关联关系;
3、商品ID与商品名称存在关联关系。
订单表里如果如果商品ID发生改变,同一个表里商品名称也要跟着改变,这样不符合第三范式,应该把商品名称这一列从订单表中删除。
反范式
什么叫反范式化设计
完全符合范式化的设计真的完美无缺吗?很明显在实际的业务查询中会大量存在着表的关联查询,而大量的表关联很多的时候非常影响查询的性能。
所谓得反范式化就是为了性能和读取效率得考虑而适当得对数据库设计范式得要求进行违反。允许存在少量得冗余,换句话来说反范式化就是使用空间来换取时间。
反范式设计-商品信息
下面是范式设计的商品信息表:
商品表id | 商品名称 | 价格 | 作者 |
---|
分类表id | 分类名称 |
---|
商品分类中间表id | 商品表id | 分类表id |
---|
商品信息和分类信息经常一起查询,所以把分类信息也放到商品表里面,冗余存放。
商品表id | 商品名称 | 价格 | 作者 | 分类名称 |
---|
范式&反范式总结
范式化设计优缺点
优点
- 范式化的更新操作通常比反范式化要快。
- 当数据较好地范式化时,就只有很少或者没有重复数据,所以只需要修改更少的数据。
- 范式化的表通常更小,可以更好地放在内存里,所以执行操作会更快。
- 很少有多余的数据意味着检索列表数据时更少需要DISTINCT或者GROUP BY语句。在非范式化的结构中必须使用DISTINCT或者GROUPBY才能获得一份唯一的列表,但是如果是一张单独的表,很可能则只需要简单的查询这张表就行了。
缺点
范式化设计的缺点是通常需要关联。稍微复杂一些的查询语句在符合范式的表上都可能需要至少一次关联,也许更多。这不但代价昂贵,也可能使一些索引策略无效。例如,范式化可能将列存放在不同的表中,而这些列如果在一个表中本可以属于同一个索引。
反范式化设计优缺点
优点
- 反范式设计可以减少表的关联
- 可以更好的进行索引优化。
缺点
- 存在数据冗余及数据维护异常。
- 对数据的修改需要更多的成本。
在实际工作中,范式和反范式往往都是混用的,需要根据实际的业务场景,进行取舍,以达到预期目标。