目前关系性数据库一共有6种范式
- 第一范式(1NF)
- 第二范式(2NF)
- 第三范式(3NF)
- 第BC范式(BCNF,也称巴斯-科德范式)
- 第四范式(4NF)
- 第五范式(5NF,也称完美范式)
通常我们设计关系型数据库的时候,满足3NF,最多满足BC范式就够了。这六级范式遵循向下包含的原则,比如符合2NF的关系模式,必定符合1NF
满足范式规定的数据库有哪些优点?
- 表结构简洁、明晰,在以后应用的过程中insert、delete、update等操作不会发生异常
- 解决数据冗余的问题,减少磁盘空间的浪费
第一范式(1NF)
( 列唯一,且不可再分)
反例:
用户编号 | 用户名称 | 地址 |
01 | 张三 | 中国北京市 |
02 | 李四 | 美国纽约市 |
上面的表中,我们看到 '地址' 这栏其实是可以再分的。在实际开发中也会遇到地址需要修改的问题。比如:现在张三想修改地址中的 '城市' ,将 北京市 修改成 上海市,但是 '国家' 却不用修改,此时后台要怎么返回数据?(当然,你也可以说前端把所有的地址信息[国家+城市]全部传过来,然后覆盖数据库中的数据就可以了。)
正例(满足第一范式):
用户编号 | 用户名称 | 国家 | 城市 |
01 | 张三 | 中国 | 北京市 |
02 | 李四 | 美国 | 纽约市 |
现在针对 '张三修改地址' 的问题就好办多了,我们想修改城市就修改城市,想修改国家就修改国家了
第二范式(2NF)
1.在第一范式的基础上
2.表中非主码属性必须完全依赖于主码
(在满足第一范式的基础上,属性完全依赖于主键,消除部分依赖)
反例:
订单编号 | 产品编号 | 产品数量 | 产品名称 | 订单价格 | 订购日期 |
01 | A001 | 2 | MySql从入门到放弃 | ¥200.00 | 2020-07-13 |
1.'订单编号'作为该表的主键
2.我们可以根据主键找到这个 '订单价格' 是¥99.9,但是这里的 '产品价格' 并没有完全依赖于主键,它也可以依赖于 '产品编号' 和 '产品数量',所以该表存在部分依赖
3. '产品价格' 并没有完全依赖于主键,不满足第二范式
正例(我们这里应该将上面的订单表拆分成两个表:订单表+产品表,用外键关联):
订单表:
订单编号 | 产品数量 | 订购日期 | 产品编号 |
01 | 2 | 2020-07-13 | A001 |
产品表:
产品编号 | 产品名称 | 产品价格 |
A001 | MySql从入门到放弃 | ¥100.00 |
第三范式(3NF)
1.在第二范式的基础上
2.非主属性不得传递依赖于主属性
(非主属性不得传递依赖于主属性)
反例:
订单编号 | 订购日期 | 客户编号 | 顾客姓名 |
001 | 2020-07 | k001 | Tom |
1.'订单编号' 作为该表的主键
2.订单编号我们能否得到订购日期、客户编号、顾客姓名,即:
订单编号 -> (订购日期,客户编号,顾客姓名)
3.我们通过订单编号,然后在通过客户编号也可以得到顾客姓名,即:
订单编号 -> (客户编号) -> (顾客姓名)
4.很明显我们这里的 '顾客姓名' 不仅通过 '订单编号' 可以获取到,通过 '客户编号' 也能获取到
正例(按照第三范式的要求,我们应该拆表,分成:订单表+客户表,顾客编号作为外键):
订单表:
订单编号 | 订购日期 | 客户编号 |
001 | 2020-07 | k001 |
客户表:
客户编号 | 顾客姓名 |
k001 | Tom |
小总结
- 第一范式的目标:确保列唯一,不可再分
- 第二范式的目标:确保表中的每列都与主键相关
- 第三范式的目标:确保表中的每列都与主键列直接相关,而不是间接相关
- 在现实开发中,如果我们完全遵循以上这三种范式去设计数据库的话基本上可以解决数据冗余的问题。但是,很多时候我们也会适当的不按照这三种范式去做,允许存在部分的数据冗余,以空间换取数据查询的时间