1.第一范式(1NF):列不可再分
1.每一列属性都是不可再分的属性值,确保每一列的原子性
2.两列的属性相近或相似或一样,尽量合并属性一样的列,确保不产生冗余数据
例:
有一个学生表,假设有两个字段分别是 name,address,而address内容写的是:江苏省南京市浦口区xxx街道xxx小区。如果这时来一个需求,需要按省市区分类,显然不符需求,这样的表结构也不是符合第一范式的。
应该设计成 name,province(省),city(市),area(区),address
2.第二范式(2NF)属性完全依赖于主键
第二范式(2NF)是在第一范式(1NF)的基础上建立起来的,即满足第二范式(2NF)必须先满足第一范式(1NF)。
第二范式(2NF)要求数据库表中的每个实例或行必须可以被惟一地区分。为实现区分通常需要为表加上一个列,以存储各个实例的惟一标识。这个惟一属性列被称为主键
每一行的数据只能与其中一列相关,即一行数据只做一件事,只要数据列中出现重复的数据,那么就要把表拆分开来。
例:
有一个订单表如下:
orderId(订单编号),roomId(房间号), name(联系人), phone(联系电话),idn(身份证)
如果这时候一个人同时订了好几个房间,就会变成一个订单编号对应多条数据,这样子联系人都是重复的,就会造成数据冗余,这时我们应该把拆分开来。
如:
订单表:
orderId(订单编号),roomId(房间号), peoId(联系人编号)
联系人表:
peoId(联系人编号),name(联系人), phone(联系电话),idn(身份证)
3.第三范式(3NF)属性不依赖于其它非主属性 属性直接依赖于主键
第二范式(3NF)是在第一范式(2NF)的基础上建立起来的,即满足第三范式(3NF)必须先满足第二范式(2NF)。
简单点意思就是对字段冗余性的约束,即任何字段不能由其他字段派生出来,它要求字段没有冗余。
例:
假设有一个员工(employee)表,它有九个属性:id
(员工编号)、name
(员工名称)、mobile
(电话)、zip
(邮编)、province
(省份)、city
(城市)、district
(区县)、deptNo
(所属部门编号)、deptName
(所属部门名称)
员工表的province、city、district依赖于zip,而zip依赖于id,换句话说,province、city、district传递依赖于id,违反了 3NF 规则。为了满足第三范式的条件,可以将这个表拆分成employee和zip两个表,如下
employee
id | name | zip |
---|---|---|
101 | 张三 | 100001 |
102 | 李四 | 200001 |
103 | 王五 | 510001 |
地区表area
zip | province | city | district |
---|---|---|---|
100001 | 北京 | 北京 | 海淀区 |
200001 | 上海 | 上海 | 静安区 |
51000 | 广东省 | 广州 | 白云区 |
为什么需要范式
数据库范式为数据库的设计、开发提供了一个可参考的典范,在许多教学材料中也是作为关键的课程内容。
那么范式的提出是为了解决什么问题?
- 第一范式,要求将列尽可能最小的分割,希望消除某个列存储多个值的冗余的行为
比如用户表中的地址信息,拆分为省、市这种明确的字段,可以按独立的字段检索、查询 - 第二范式,要求唯一的主键,且不存在对主键的部分依赖,希望消除表中存在冗余(多余)的列
比如订单表中的商品分类、详情信息,只需要由商品信息表存储一份即可。 - 第三范式,要求没有间接依赖于主键的列,即仍然是希望消除表中冗余的列
比如用户表中不需要存储额外的 其所在城市的人口、城市特点等信息。
很明显,这些范式大都是为了消除冗余而提出的,即尽可能的减少存储成本。
没有冗余的数据库设计可以做到。但是,没有冗余的数据库未必是最好的数据库,有时为了提高运行效率,就必须降低范式标准,适当保留冗余数据。具体做法是: 在概念数据模型设计时遵守第三范式,降低范式标准的工作放到物理数据模型设计时考虑。降低范式就是增加字段,允许冗余。