概述
关系型数据库进行设计的时候,一般需要遵循三大范式,第一范式要求确保表中每列的原子性,也就是不可拆分;第二范式要求确保表中每列与主键相关,而不能只与主键的某部分相关(主要针对联合主键),主键列与非主键列遵循完全函数依赖关系,也就是完全依赖;第三范式确保主键列之间没有传递函数依赖关系,也就是消除传递依赖。
第一范式(1NF)
所谓第一范式(1NF)是指在关系模型中,对于添加的一个规范要求,所有的域都应该是原子性的,即数据库表的每一列都是不可分割的原子数据项,而不能是集合,数组,记录等非原子数据项。即实体中的某个属性有多个值时,必须拆分为不同的属性。
- 示例:
设计一个存储单位员工信息的表格。
第一种表的设计:
员工id | 姓名 | 性别 | 年龄 | 电话 | 职位 |
---|---|---|---|---|---|
1 | 王工 | 男 | 22 | 5678 | 研发部C++工程师 |
2 | 高工 | 女 | 21 | 6578 | 研发部Java工程师 |
在上面的表中,职位信息可以拆分为部门和职位,故不满足第一范式,调整后的表设计:
员工id | 姓名 | 性别 | 年龄 | 电话 | 部门 | 职称 |
---|---|---|---|---|---|---|
1 | 王工 | 男 | 22 | 5678 | 研发部 | C++工程师 |
2 | 高工 | 女 | 21 | 6578 | 研发部 | Java工程师 |
调整后的每一列都不可再分,满足第一范式(1NF)
第二范式(2NF)
第二范式(2NF)要求实体的属性完全依赖于主关键字。所谓完全依赖是指不能存在仅依赖主关键字一部分的属性,如果存在,那么这个属性和主关键字的这一部分应该分离出来形成一个新的实体,新实体与原实体之间是一对多的关系。为实现区分通常需要为表加上一个列,以存储各个实例的唯一标识。简而言之,第二范式就是在第一范式的基础上属性完全依赖于主键。
- 示例
设计一个订单信息表,订单有多种商品,将订单编号和产品编号作为联合主键。
可以发现,产品数量、产品折扣、产品价格与“订单号”和“产品号”都相关,但是订单金额和订单时间仅与“订单号”相关,与“产品号”无关,这样就不满足第二范式的要求。调整如下,拆分成两个表格
第三范式(3NF)
确保每个属性都直接依赖主键,而不是依赖于其它非主属性,即在2NF的基础之上消除传递依赖。
- 示例:
所有属性都完全依赖于学号,所以满足第二范式,但是“班主任性别”和“班主任年龄”直接依赖的是“班主任姓名”,而不是主键“学号”,所以需做如下调整:
这样就满足了第三范式
目的原则
-
规范化目的是使结构更合理,消除存储异常,使数据冗余尽量小。便于插入、删除和更新
-
遵从概念单一化“一事一地”原则,即一个关系模式描述一个实体或实体间的一种联系。规范的实质就是概念的单一化。
满足范式就是最优的吗?
规范化的优点是明显的,它避免了大量的数据冗余,节省了存储空间,保持了数据的一致性。当一个库里的数据经常发生变化时,达到3NF的库可以使用户不必在超过两个以上的地方更改同一个值。
那么是不是只要把所有的表都规范为3NF后,数据库的设计就是最优的呢?这可不一定。
范式越高意味着表的划分更细,一个数据库中需要的表也就越多,用户不得不将原本相关联的数据分摊到多个表中。当用户同时需要这些数据时只能采用连接表的形式将数据重新合并在一起。同时把多个表联接在一起的花费是巨大的,尤其是当需要连接的两张或者多张表数据非常庞大的时候,表连接操作几乎是一个噩梦,这严重地降低了系统运行性能。