数据库有六种范式,一级比一级更加严格,一般来说数据库满足第二/三范式就够了。各大教科书上对范式的定义都比较抽象,建议结合实例理解各大范式的含义,再回顾定义透彻理解。
第一范式:在关系模型中,对于添加的一个规范要求,所有的域都应该是原子性的,即数据库表的每一列都是不可分割的原子数据项,而不能是集合,数组,记录等非原子数据项。
简单理解就是列不可分割,比如产品表中,产品数量和产品价格要各为一列,笼统定义一列产品包含两个属性是不恰当的。
第二范式:在1NF的基础上,非码属性必须完全依赖于候选码(在1NF基础上消除非主属性对主码的部分函数依赖)。
候选码即可以唯一标识元组/记录的属性组。例如员工表中员工号可以区分不同的员工/记录,员工号可以作为候选键,员工号确定了,该员工姓名、年龄、性别等其他非码属性也就确定了。任何一个候选键都可以被选作主键。在找不到候选键时,可额外增加属性以实现区分。
第三范式:在2NF基础上,任何非主属性不依赖于其它非主属性(在2NF基础上消除传递依赖)。
例如有一个学生表包含学生学号、姓名、年龄、学生属于的系编号,系的名称、地址等信息。学生表以学生学号为主键,那么系名称和系地址依赖于系编号(deptNo→location),系编号依赖于学号(stuNo→deptNo),对于这张表来说,系名称和系地址传递依赖于主属性学生学号,直接依赖于非主属性系编号,不符合第三范式。根据第三范式(3NF)应该构建专门的系信息表,也避免了大量的数据冗余。
Boyce-Codd Normal Form(BCNF巴斯-科德范式):在3NF基础上,任何非主属性不能对主键子集依赖(在3NF基础上消除对主码子集的依赖)。
这里用一个经典的仓库表例子解释:
某公司有若干个仓库,每个仓库只能有一名管理员,一名管理员只能在一个仓库中工作,一个仓库中可以存放多种物品,一种物品也可以存放在不同的仓库中。每种物品在每个仓库中都有对应的数量。
已知函数依赖集:
- 仓库名 → 管理员,管理员 → 仓库名,(仓库名,物品名)→ 数量
- 候选码:(管理员,物品名)和(仓库名,物品名)
- 主属性:仓库名、管理员、物品名
可以看出仓库表符合第三范式。那么会有什么问题呢?
新增一个空仓库,是否可以为该仓库指派管理员?——不可以,因为物品名也是主属性,根据实体完整性的要求,主属性不能为空。
某仓库被清空后,需要删除所有与这个仓库相关的物品存放记录,会带来什么问题?——仓库本身与管理员的信息也被随之删除了。
仓库表符合第三范式,但是不符合BCNF范式,由于主属性仓库名依赖于部分主属性(管理员、物品名)
修改为两张表:仓库表(仓库名,管理员)和库存表(仓库名,物品名,数量),上述的问题就不存在了。
第四范式需要满足以下要求:(1)必须满足第三范式(2)表中不能包含一个实体的两个或多个互相独立的多值因子。
第五范式有以下要求:(1)必须满足第四范式(2)表必须可以分解为较小的表,除非那些表在逻辑上拥有与原始表相同的主键。
四五范式一般设计不用考虑。
总结:在范式化的数据库中,每个事实数据会出现一次并且只出现一次。相反,在反范式化的数据库中,信息是冗余的,可能存储在多个地方。
范式化的更新经常比反范式化快,而且范式化的表通常更小,便于放在内存中操作,检索数据更少地使用 distinct或group by。缺点是通常需要关联查询。
反范式化的表能避免关联,单独的表也能使用更有效的索引策略。
实际中根据具体情况混用范式化和反范式化,反范式化由于有数据冗余,可以在增删改时配合触发器保证数据的一致性。