良好的表结构设计是高性能的基石,应该根据系统将要执行的业务查询来设计,这往往需要权衡各种因素。糟糕的表结构设计,会浪费大量的开发时间,严重延误项目开发周期,让人痛苦万分,而且直接影响到数据库的性能,并需要花费大量不必要的优化时间,效果往往还不怎么样。
在数据库表设计上有个很重要的设计准则,称为范式设计。
范式来自英文Normal Form
,简称NF
。MySQL是关系型数据库,但是要想设计—个好的关系,必须使关系满足一定的约束条件,此约束已经形成了规范,分成几个等级,一级比一级要求得严格。满足这些规范的数据库是简洁的、结构明晰的,同时,不会发生插入(insert)、删除(delete)和更新(update)操作异常。反之则是乱七八糟,不仅给数据库的编程人员制造麻烦,而且面目可憎,可能存储了大量不需要的冗余信息。
目前关系数据库设计有六种范式:第一范式(1NF)、第二范式(2NF)、第三范式(3NF)、巴斯-科德范式(BCNF)、第四范式(4NF)和第五范式(5NF,又称完美范式)。满足最低要求的范式是第一范式(1NF)。在第一范式的基础上进一步满足更多规范要求的称为第二范式(2NF),其余范式以次类推。一般来说,数据库只需满足第三范式(3NF)就行了(也就是前三个范式,因为后面的范式是在前面的范式演变过来的,后面的范式包括前面的范式)。
定义: 属于第一范式关系的所有属性都不可再分,即数据项不可分。
理解: 第一范式强调数据表的原子性,是其他范式的基础。
将数据库表分为一个单一独立的属性列,比如姓名、年龄、性别、联系电话等等,这便是第一范式,数据库表的原子性。
上表就符合第一范式关系。但日常生活中仅用第一范式来规范表格是远远不够的,依然会存在数据冗余过大、删除异常、插入异常、修改异常的问题,此时就需要引入规范化概念,将其转化为更标准化的表格,减少数据依赖。
实际上,1NF是所有关系型数据库的最基本要求,你在关系型数据库管理系统(RDBMS),例如SQL Server,Oracle,MySQL中创建数据表的时候,如果数据表的设计不符合这个最基本的要求,那么操作一定是不能成功的。也就是说,只要在RDBMS中已经存在的数据表,一定是符合1NF的。
第二范式(2NF)是在第一范式(1NF)的基础上建立起来的,即满足第二范式(2NF)必须先满足第一范式(1NF)。
第二范式(2NF)要求数据库表中的每个实例或行必须可以被惟一地区分。通常在实现来说,需要为表加上一个列,以存储各个实例的惟一标识。例如员工信息表中加上了员工编号(emp_id)列,因为每个员工的员工编号是惟一的,因此每个员工可以被惟一区分。这个惟一属性列被称为主关键字或主键、主码。
也就是说要求表中只具有一个业务主键,而且第二范式(2NF)要求实体的属性完全依赖于主关键字。所谓完全依赖是指不能存在仅依赖主关键字一部分的属性。什么意思呢?
这句话的意思是一张业务表,要和另外的一张业务表有所联系,那么最最应该设计的是中间表,中间表存储了两张业务表的业务关键字。
要特别特别注意:这里的业务关键字可不是指 id 主键,比如员工表里面的员工编号,或者部门表里面的部门编号,这些都是业务关键字段。
这个中间表具体的作用是一个信息包含了多个附属信息,就可以采用第二范式中间表来存储数据,比如一个产品会有多个订单,或者一个订单里面有多个产品等等。
————
| 中间表 |
————
| 1 号产品 id | 1 号订单id |
| 1 号产品 id | 2 号订单id |
————————————
| 1 号订单 id | 1 号产品id |
| 1 号订单 id | 2 号产品id |
指每一个非主属性既不部分依赖于也不传递依赖于业务主键,也就是在第二范式的基础上消除了非主键对主键的传递依赖。例如,存在一个部门信息表,其中每个部门有部门编号(dept_id)、部门名称、部门简介等信息。那么在员工信息表中列出部门编号后就不能再将部门名称、部门简介等与部门有关的信息再加入员工信息表中。如果不存在部门信息表,则根据第三范式(3NF)也应该构建它,否则就会有大量的数据冗余。
具体的设计:
通常遇到一些数据项,会包含了其他数据库表的 id 编号等等,详细的情况是,一对多,但是本身却是多对一,怎么理解这个意思?
这里的 一对一,指的是,该数据只有一个数据项对应一个数据项,但是另外的一个数据项却能对应多个数据项,比如员工只属于一个部门,而一个部门却可以有几百个员工对应。
借用一下图来解释更清楚,下面这张图中,第一张订单表是错误的,产品名称不能接着写进去订单表里面,需要创建一个产品表来存储,不然在订单表里面继续添加产品名称列,那不正是数据冗余吗?多次一举。
把订单表里的产品名称删除了,只剩下产品 id 就满足第三范式。
数据库表结构及索引设计
数据库表的设计