范式适用于数据库结构优化,能帮助我们去除冗余属性,优化表结构
但是开始之前,先要明确一点:范式并不是级别越高越好,有时过高级别范式甚至会影响数据库性能(在反规范化中说)
三种范式
1NF
第一范式:即对属性要求 原子性,如学生(学号,姓名,性别,出生年月日),出生年月日若还可以拆分为具体的年、月、日,则不是第一范式
在 DBMS 中是不会出现违反第一范式的情况的,因为默认就不能将某一列再细分
2NF
第二范式:要求记录的 唯一性,什么意思呢?就是使一张表中每条记录都是不同的,不会出现修改某一属性值却要改动一大堆数据的情况。
首先满足 1NF,另外包含两部分内容,一是表必须有一个主键;二是没有包含在主键中的列必须完全依赖于主键,而不能只依赖于主键的一部分。
要保证符合 2NF,则需要消灭表中的 局部依赖,打个比方:考虑一个订单明细表:
【OrderDetail】(OrderID,ProductID,UnitPrice,Discount,Quantity,ProductName)。 因为我们知道在一个订单中可以订购多种产品,所以单单一个 OrderID 是不足以成为主键的,主键应该是(OrderID,ProductID)。显而易见 Discount(折扣),Quantity(数量)完全依赖(取决)于主键(OderID,ProductID),而 UnitPrice,ProductName 只依赖于 ProductID。所以 OrderDetail 表不符合 2NF。不符合 2NF 的设计容易产生冗余数据。 可以把【OrderDetail】表拆分为【OrderDetail】(OrderID,ProductID,Discount,Quantity)和【Product】(ProductID,UnitPrice,ProductName)来消除原订单表中UnitPrice,ProductName多次重复的情况。
第二范式(2NF)要求实体的属性完全依赖于主关键字(完全函数依赖)。所谓完全依赖是指不能存在仅依赖主关键字一部分的属性,如果存在,那么这个属性和主关键字的这一部分应该分离出来形成一个新的实体,新实体与原实体之间是一对多的关系。为实现区分通常需要为表加上一个列,以存储各个实例的唯一标识。简而言之,第二范式就是在第一范式的基础上属性完全依赖于主键。
3NF
第三范式:第三范式防的是 非主属性传递依赖,打个比喻:如果你女朋友有条舔狗,天天赖在你女朋友后面,这样就形成了 你 <-- 你女朋友 <-- 舔狗 的依赖链,那肯定是不行的,得把舔狗刀了
在上例中,将你看作主键,舔狗和女朋友实际上就是非主键。3NF 中只允许非主属性依赖于主属性,而舔狗身为一个非主属性依赖于另一个非主属性,所以舔狗必须死
这样一来,就消除了传递依赖。
如果不符合 3NF 可能会存在问题:
数据冗余:有重复值;更新异常:有重复的冗余信息,修改时需要同时修改多条记录,否则会出现数据不一致的情况 。
实际上除了这三种还有:BCNF(改进 3NF),4NF,5NF,DKNF,6NF
但是一般说来,数据库只需满足第三范式(3NF)就行了。
反规范化处理
毕竟之前我们说过,过高的范式程度反而不利于数据库操作,降低范式程度,可以实现以空间换时间的操作(虽然有冗余数据但联表操作减少)
规范化减少了 数据冗余,易于保证 数据的完整性,但规范化也会导致 数据库性能降低,因此,在利用规范化设计数据库时要平衡两者的关系。
所谓的反规范化,就是适当降低甚至抛弃范式约束,不再要求一个表只表述其表自身,而是适当冗余性添加带有某种依赖关系的数据。
主要处理手段
(1)增加冗余列或派生列
如果应用系统的 常用操作 需要 关联其他表中的数据,则在进行表设计时,应直接将该列融入当前表中,使其冗余存在,称为冗余列
(2) 表的合并和分割
执行反规范化设计,表的数量往往也就会 减少,而这也就 降低了表连接运算的压力,可以有力提升性能
带来问题
一本万利的事情客观上是不存在的,反规范化也是如此:
- 数据冗余的存在
- 降低了数据库的完整性