范式
在关系数据库中,设计数据库时遵从一定的规范要求,可以使得数据库冗余变小。 目前关系数据库有六种范式,不过查了查资料,其实是十种。
- 1NF 第一范式
- 2NF 第二范式
- 3NF 第三范式
- EKNF 主键范式
- BCNF Boyce–Codd 范式
- 4NF 第四范式
- ETNF 关键元组范式
- 5NF 第五范式
- DKNF 域键范式
- 6NF 第六范式
不过在这里呢,不讲 EKNF 和 ETNF,以八大范式的形式来讲。
1 | 2 | 3 | BC | 4 | 5 | DK | 6 | |
---|---|---|---|---|---|---|---|---|
主键 | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ |
没有重复组 | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ |
字段原子性 | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ |
没有部分函数依赖 | ❌ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ |
没有传递函数依赖 | ❌ | ❌ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ |
去除主属性对不必要的函数依赖 | ❌ | ❌ | ❌ | ✔️ | ✔️ | ✔️ | ✔️ | - |
每个非平凡的多值依赖都有一个超键 | ❌ | ❌ | ❌ | ❌ | ✔️ | ✔️ | ✔️ | - |
超键是每个显式连接依赖的一部分 | ❌ | ❌ | ❌ | ❌ | ❌ | ✔️ | ✔️ | - |
候选键隐含了每个非平凡的连接依赖关系 | ❌ | ❌ | ❌ | ❌ | ❌ | ✔️ | ✔️ | - |
每个约束都是域约束和键约束的结果 | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✔️ | - |
每个连接依赖都是平凡的 | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✔️ |
✔️: 符合 ❌: 不符合 -: 不适用
1NF
在上表中可以看到,第一范式要求数据库的每个列的值域都是由原子值组成;每个字段的值都只能是单一值,当然还要有主键。
重复组
顾客 | 日期 | 消费 |
---|---|---|
张三 | 1970-01-01 | 80.00 90.00 10.00 |
李四 | 1970-01-01 | 16.00 |
王五 | 1970-01-01 | 3.00 |
这种情况下就是不符合第一范式的,消费就是重复组。想要消除重复组的话,只要把每笔记录都转化为单一记录即可:
顾客 | 日期 | 消费 |
---|---|---|
张三 | 1970-01-01 | 80.00 |
张三 | 1970-01-01 | 90.00 |
张三 | 1970-01-01 | 10.00 |
李四 | 1970-01-01 | 16.00 |
王五 | 1970-01-01 | 3.00 |
主键
上表是没有主键的,如果同一天同一个人消费呢同样的金额,这样的交易做了两次,这个表就有点乱了。
因为这两笔交易是一模一样的,也就是说如果只靠这些数据没有办法分辨这两笔记录。
之所以说它不符合第一范式,是因为上面这样的表示法欠缺一个唯一标识符,而且可以保证在这个数据中唯一标识符不会重复出现。
交易 | 顾客 | 日期 | 消费 |
---|---|---|---|
1 | 张三 | 1970-01-01 | 80.00 |
2 | 张三 | 1970-01-01 | 90.00 |
3 | 张三 | 1970-01-01 | 10.00 |
4 | 李四 | 1970-01-01 | 16.00 |
5 | 王五 | 1970-01-01 | 3.00 |
字段原子性
有时候为了方便,可能设计数据库的时候会这样。
姓名 | 联系方式 |
---|---|
张三 | 12345678901, 12345@mail.com |
张三 | 12345678902 |
但是这样就违反了字段的原子性,改成这样会更好。
姓名 | 手机号 | 邮箱 |
---|---|---|
张三 | 12345678901 | 12345@mail.com |
张三 | 12345678902 |
再加上主键吧。
编号 | 姓名 | 手机号 | 邮箱 |
---|---|---|---|
0 | 张三 | 12345678901 | 12345@mail.com |
1 | 张三 | 12345678902 | |
2 | 李四 | 12345678903 | 12346@mail.com |
2NF
第二范式在第一范式的基础上增加了一点要求,数据表里的所有数据都要和该数据表的键(主键与候选键)有完全依赖关系: 每个非键属性必须独立于任意一个候选键的任意一部分属性。 如果有哪些数据只和一个键的一部分有关的话,就得把它们独立出来变成另一个数据表。
商品 ID(Primary key) | 价格 | 供应商 ID(Primary key) | 供应商名称 |
---|---|---|---|
1 | 10.00 | 1 | 食品公司 |
2 | 20.00 | 1 | 食品公司 |
3 | 33.33 | 2 | 饮料公司 |
这个数据表的每个值都是单一值,所以它符合第一范式。 因为同一个商品有可能由不同的供应商提供,所以得把商品 ID 和供应商 ID 合在一起组成一个主键。
商品和价格之间的关系很正确:同一个商品在不同供应商有可能会有不同的报价,所以价格确实和主键完全相关。
另一方面,供应商的名称只和供应商 ID 有关,这不符合第二范式的原则。
商品 ID | 价格 | 供应商 ID |
---|---|---|
1 | 10.00 | 1 |
2 | 20.00 | 1 |
3 | 33.33 | 2 |
供应商 ID | 供应商名称 |
---|---|
1 | 食品公司 |
2 | 饮料公司 |
3NF
第三范式要求所有非主键属性都只和候选键有相关性,也就是说非主键属性之间应该是独立无关的。
学号 | 姓名 | 年龄 | 所在学院 | 学院地点 | 学院电话 |
---|---|---|---|---|---|
1 | 张三 | 1 | 语言文化 | 一路向西 | 12345678 |
2 | 张三 | 1 | 语言文化 | 一路向西 | 12345678 |
3 | 李四 | 2 | 科学技术 | 一路向南 | 12345679 |
这个数据表就是符合第二范式但不符合第三范式的,因为其中存在 (学号) → (所在学院) → (学院地点, 学院电话)
的关系。 它存在非关键字段"学院地点"、"学院电话"对关键字段"学号"的传递函数依赖。
学号 | 姓名 | 年龄 | 学院编号 |
---|---|---|---|
1 | 张三 | 1 | 1 |
2 | 张三 | 1 | 1 |
3 | 李四 | 2 | 2 |
学院编号 | 学院名称 | 学院地点 | 学院电话 |
---|---|---|---|
1 | 语言文化 | 一路向西 | 12345678 |
2 | 科学技术 | 一路向南 | 12345679 |
BCNF
如果对第三范式做进一步加强,那就是 Boyce-Codd 范式了。 BC 范式去除了属性间的不必要的函数依赖。
商店 ID | 商品 ID | 数量 | 店长 |
---|---|---|---|
1 | 1 | 88 | 张三 |
1 | 2 | 99 | 张三 |
2 | 3 | 7107 | 李四 |
2 | 4 | 5511 | 李四 |
2 | 5 | 4114 | 李四 |
对于这张表,存在如下关系:
- 一个商店对应一个店长
- 一个商店有多个商品
想让其符合 BCNF,可以改成这样。
商店 ID | 商品 ID | 数量 |
---|---|---|
1 | 1 | 88 |
1 | 2 | 99 |
2 | 3 | 7107 |
2 | 4 | 5511 |
2 | 5 | 4114 |
商店 ID | 店长 |
---|---|
1 | 张三 |
2 | 李四 |
4NF
前几个方式都是关注属性集合之间的函数依赖,而第四范式关注更一般形式。 第四范式能作用到的范围有限,不是什么场景都能用得到的。 而且比较扯。
学生 | 课程 | 爱好 |
---|---|---|
张三 | 语文 | 篮球 |
张三 | 语文 | 网球 |
张三 | 数学 | 网球 |
李四 | 语文 | 网球 |
王五 | 语文 | 网球 |
学生 | 课程 |
---|---|
张三 | 语文 |
张三 | 数学 |
李四 | 语文 |
王五 | 语文 |
学生 | 爱好 |
---|---|
张三 | 篮球 |
张三 | 网球 |
李四 | 网球 |
王五 | 网球 |
5NF
第五范式去除多个关系之间的语义相关。
客户 | 品牌 | 产品 |
---|---|---|
张三 | ABC | S |
张三 | ABC | G |
张三 | KCF | W |
李四 | KCF | W |
李四 | ABC | G |
如果数据表在 4NF 中并且不包含任何连接依赖关系并且连接应该是无损的,则关系在 5NF 中。当所有表都被分成尽可能多的表以便避免冗余时,满足 5NF。
客户 | 品牌 |
---|---|
张三 | ABC |
张三 | KCF |
李四 | KCF |
李四 | ABC |
客户 | 产品 |
---|---|
张三 | S |
张三 | G |
张三 | W |
李四 | W |
李四 | G |
品牌 | 产品 |
---|---|
ABC | S |
ABC | W |
ABC | G |
KCF | W |
ABC | G |
DKNF
DKNF 是去除关系不包含于域约束的其他约束。
如果经验与等级的换算是 等级 * 100
,类型有 NPC 和 Player。这张表就是违反 DKNF 的。
玩家 | 类型等级 | 经验值 |
---|---|---|
张三 | MPC.3 | 322 |
李四 | Player.8 | 858 |
王五 | Player.4 | 425 |
可以分离为两张表。
玩家 | 类型 | 经验值 |
---|---|---|
张三 | MPC | 322 |
李四 | Player | 858 |
王五 | Player | 425 |
等级 | 经验 |
---|---|
3 | 300 |
8 | 800 |
4 | 400 |
6NF
第六范式,所谓每个连接依赖都是平凡的,通俗来说就是每个表都只能拥有一个主键和不超过一个的属性。 这就意味着,每张表的列数非常的少,除了主键之外,最多只能拥有一列。 看到这个定义之后,是不是有点 No-SQL key-value 数据库的意思。
数据是随着时间不断变化的,也就是我们常说的一句谚语:唯一不变的只有变化本身。 这就要求数据库设计最好是模块化,比较灵活,而且很容易能够跟踪数据的变化。 很可惜,现有的五范式并不能满足这些要求。
这里并不讲第六方式,不是因为别的,因为没这么接触过,不甚了解。
真正的范式
如果你知道自己在做什么,就去做吧,不要让规则束缚了你自己