范式:英文名称是 Normal Form,它是英国人 E.F.Codd(关系数据库的老祖宗)在上个世纪70年代提出关系数据库模型后总结出来的,范式是关系数据库理论的基础,也是我们在设计数据库结构过程中所要遵循的规则和指导方法。目前有迹可寻的共有8种范式,依次是:1NF,2NF,3NF,BCNF,4NF,5NF,DKNF,6NF。通常所用到的只是前三个范式,即:第一范式(1NF),第二范式(2NF),第三范式(3NF)
之所以使用范式,往往是设计不规范的数据库表可能造成大量的数据冗余,也可能在发生插入、删除、修改操作后出现各种各样的不合理的问题。
1)1NF(第一范式):
数据库表的每一列都是不可分割的基本数据项。 如“电话号码”这个属性可以继续被分割为“办公电话”、“手机号码”等属性,在第一范式的语义下不应该被作为单独的一列出现。
2)2NF(第二范式):
必须先满足第一范式。 数据库表中的每一行必须可以被唯一的区分,即每一行中有一个唯一标识将这行与其他行区分出来,这个唯一标识就是我们常说的主键。在2NF的语义下,所有非主键的字段都要依赖主键。比如在学生表中我们用学生id作为主键,那么当我们需要查询一个学生的时候,通过他的id号应该可以唯一地定位到这个学生,会并且只会查出一行。
3)3NF(第三范式):
必须先满足第二范式。非主键字段都与主键字段有直接依赖关系,不存在传递依赖。可以理解为非主键字段只依赖主键字段,而不依赖其它的非主键字段。
比如员工表的字段构成为:员工id(主键),姓名,性别,年龄,所属部门,部门经理姓名,部门电话。
这里所有的非主键字段并不是直接依赖于主键“员工id”, 可以看到“部门经理姓名”和“部门电话”这两个属性依赖于“所属部门”,而“所属部门”又依赖于主键“员工id”,这就是传递依赖,这里可将该员工表的表结构改成:员工id(主键),姓名,性别,年龄,部门名称
单独提出部门表结构为:部门名称(主键),部门经理id ,部门电话
4)BCNF :第三范式的扩展和加强。
这里用网上看到的一个例子:
表结构: 仓库id 管理员id 物品id 物品数量
其中管理员和仓库的关系是一对一,仓库和物品的关系是一对多;
这个设计符合第一、二、三范式,即每列不可分割、主键唯一且不存在传递依赖;
可看到依赖关系有 管理员id 依赖 仓库id, 物品id
依赖 仓库id。 即存在主键到主键再到非主键的传递依赖关系。
这里的主要问题是,仓库id和管理员id这两个关键字段之间的关系被耦合到每一个实例中了,这导致:
a) 表中没有数据的时候,无法描述仓库和管理员之间的关系
b) 一个仓库的管理员替换后,表中所有含有该仓库的实例中的管理员id都要被修改
解决办法是将二者的关系提出来单独建表。
则原表结构改为:仓库id 物品id 物品数量
增加表的结构: 仓库id 管理员id
这样 仓库和管理员的关系 及 仓库和物品的关系 就解耦合了。
5)4NF(第四范式):
必须先满足第三范式。简单来说就是将表中的多值属性拆分出来,分别建表。
比如在用户表中有一个非主键字段“电话号码”,某一行实例的“电话号码”内容可能是手机号码,可能是座机号码,也可能是多个内容的直接组合(如“电影”属性中填“动作,喜剧,科幻”), 这就是多值属性。
例:
用户表包含一个多值字段“爱好”,某个实例内容可能为:
用户id 姓名 爱好
1 John 足球、游泳、植物大战僵尸
这种情况可以再单独拆出一个爱好表,如下:
字段: 用户id 爱好
实例: 1 足球
实例: 1 游泳
实例: 1 植物大战僵尸
实例: 2 乒乓球
实例: 3 野外求生
在网上看到一种方案是直接加一个字段来约束多值属性内容,如下:
字段: 用户id 电话类别 电话号码
实例: 1 手机 12345678
实例: 2 家庭座机 020-123456
实例: 3 手机 22345678
这种设计不符合第三范式,电话类别这个属性和主键是没有依赖关系的,它仅仅用来约束电话号码的内容。 优点是较上述解决办法可以减少多表查询的开销。
范式是人们在具体的业务场景中遇到问题逐渐总结出来的通用解决思路,在实际开发中如果并不常用到某些范式,可能只是问题和数据的规模还没达到。