假设有一张用户信息表,上面除了用户编号、姓名之外,还会记录地址信息:
在这里面,地址信息一栏就是不符合第一范式(1NF)的。
第一范式(1NF):数据库表的每一列都是不可分割的原子项
因此,应该拆分为:
以一个订单表为例,通常在淘宝上下单时会产生包含多个商品的订单,如下:
这里同样违反了第二范式的定义: 第二范式(2NF):每个表必须有且仅有一个数据元素为主键(Primary key),其他属性需完全依赖于主键
第二范式需建立在满足第一范式的基础之上
第二范式首先要求的是存在一个唯一的主键,在上面的表中,就必须将 订单号、商品号 作为一个联合的主键才能满足要求。 那么对于第二点要求呢? 其他属性是否依赖于这个主键? 在订单的场景中,我们可以认为这算是合理的,因为商品的价格甚至名称都可能会发生变化,而在每个订单中所看到的这些信息都应该是不变的,谁也不希望看到自己已经支付的订单中的商品信息突然大降价,当然更重要的还是保持订单总价与商品单价记录的一致性。 因此这里的记录可以认为是商品信息在创建订单时的一个快照。
但是,对于下面的这一场景可能就不合适了:
商品所属的类别一般是固定的,也就是商品的类别属性仅仅与商品编号相关,即仅仅是依赖于主键的一部分。 这就违反了第二范式中“其他属性必须完全依赖于主键"的规则,因此需要将该属性分离到商品信息表中。
让我们回到一开始的用户表,如果在用户信息表中,同时补充一些城市的信息:
这样便违反了第三范式的定义:
第三范式(3NF):数据表中的每一列都和主键直接相关,而不能间接相关
同样,第三范式也需要建立在第二范式的基础之上
很明显,这里的城市人口、特色等属性都仅仅依赖于用户所在的城市,而不是用户,只能算间接的关系。 因此最好的做法是将城市相关的属性分离到一个城市信息表中。
数据库范式为数据库的设计、开发提供了一个可参考的典范,在许多教学材料中也是作为关键的课程内容。 那么范式的提出是为了解决什么问题?
很明显,这些范式大都是为了消除冗余而提出的,即尽可能的减少存储成本。
借助三范式的理念,你可以设计出很精炼的数据库表结构。然而现有的项目应用并不会完全遵循范式的理念,原因在于:
Q.E.D.