我们在设计数据库时考虑的因素包括读取性能,数据一致性,数据冗余度,可扩展性等,好好学习数据库规范化的知识,设计的数据库表看起来才专业。
范式一览
“键”理解:
- 超键:在关系中能唯一标识元组的属性集称为关系模式的超键
- 候选键:不含有多余属性的超键称为候选键。也就是在候选键中,若再删除属性,就不是键了。
- 主键:用户选作元组标识的一个候选键称为主键。
- 外键:如果关系模式R中属性K是其它模式的主键,那么K在模式R中称为外键。
主键为候选键的子集,候选键为超键的子集,而外键的确定是相对于主键的!
0NF(Not Normal Form)
一个员工有多个电话,用“,”分割,我们常常这样设计,的确这样设计没毛病,干嘛再设计一个写的电话表,还要关联,浪费时间。的确,我们有些情况下确实这样设计。但是他不符合数据库的规范,为什么呢:
- 首先看Phone,字段长度不明确,我们应该定义多长,长了浪费,短了业务不允许。
- 统一每个人有多少电话,没法统计。
- 可维护性差,我删除个电话,更换个电话不好弄。
- 扩展性差,哪个是主,哪个是次,哪个是家庭,哪个是工作电话怎么搞。
所以就有了1NF,设计上还是不要有分割,保持原子性。
EmployeeId | Person | Phone |
---|---|---|
1 | John Jones | 27716602, 26153215, |
2 | Peter Janes | 47716602, 36153215, 52321262 |
3 | Michael Juines | 17716602 |
1NF(First Normal Form)
定义:第一范式是指数据库的每一列都是不可分割的基本数据项,强调列的原子性。
我们改造一下上边的表结构,这样就符合了第一范式:
EmployeeId | Person | Phone |
---|---|---|
1 | John Jones | 27716602 |
1 | John Jones | 26153215 |
2 | Peter Janes | 47716602 |
2 | Peter Janes | 36153215 |
2 | Peter Janes | 52321262 |
3 | Michael Juines | 17716602 |
上边第一范式的设计好吗?不太优雅吧,我们有太多的冗余,如果现在Peter有3个电话占了三行,若有Peter有100个电话呢,是不是要有100行一样的EmployeeId和Person。可能你会说,也占用不了太多的空间呀,那我要问你Peter还有100多个属性呢,所以说,第一范式是没有考虑数据冗余的。我们要消灭数据冗余,就要考虑2NF。
2NF(Second Normal Form )
定义:第二范式建立在第一范式的基础上,即满足第二范式一定满足第一范式,第二范式要求数据表每一个实例或者行必须被唯一标识。除满足第一范式外还有两个条件,一是表必须有一个主键;二是没有包含在主键中的列必须完全依赖于主键,而不能只依赖于主键的一部分。
上表的主键现在是什么,竟然是EmployeeId和Phone组成的联合主键,而Name只依赖于EmployeeId。这就不符合Employee的建表目的了,name只依赖于EmployeeId就好,Phone在这凑什么热闹,把Phone单独建张表吧:
EmployeeId | Name |
---|---|
1 | John |
2 | Peter |
3 | Michael |
PhoneId | Phone |
---|---|
1 | 27716602 |
2 | 26153215 |
3 | 47716602 |
4 | 36153215 |
5 | 52321262 |
6 | 17716602 |
EmployeeId | PhoneId |
---|---|
1 | 1 |
1 | 2 |
2 | 3 |
2 | 4 |
2 | 5 |
3 | 6 |
3NF(Third Normal Form)
定义: 若某一范式是第二范式,且每一个非主属性都不传递依赖于该范式的候选键,则称为第三范式,即不能存在:非主键列 A 依赖于非主键列 B,非主键列 B 依赖于主键的情况。
第三范式其实也和第二范式一个目的,减少数据冗余。为了说明第3范式,我们增加个邮编地址,如下表所示。
EmployeeId | Name | ZIP | City |
---|---|---|---|
1 | John | 8000 | Aarhus C |
2 | Peter | 8200 | Aarhus N |
3 | Michael | 8520 | Lystrup |
上表明显满足第二范式,但是是不是感觉City多余,明明非主属性ZIP知道了就知道非主属性EmployeeId在哪个城市了。好了,这里出来了一个传递依赖,消灭掉如下:
EmployeeId | Name | ZIP |
---|---|---|
1 | John | 8000 |
2 | Peter | 8200 |
3 | Michael | 8520 |
ZIP | City |
---|---|
8000 | Aarhus C |
8200 | Aarhus N |
8520 | Lystrup |
BCNF(Boyce and Codd Normal Form)
下表满足:
- 1个学生可以选择多门课程,比如101选了java和C++。
- 一个教授教一门课程,可以有多个教授教一门课程,比如Java对应三个教授。
studentId | subject | professor |
---|---|---|
101 | Java | P.Java |
101 | C++ | P.Cpp |
102 | Java | P.Java2 |
103 | C# | P.Chash |
104 | Java | P.Java |
该设计满足3NF,因为主键为studentId和subject,满足1NF,所有值都是原子;满足2NF,不存在部分依赖。满足3NF,不存在传递依赖。但是不是BCNF,原因如下:
professor->subject
subject是一个主属性,而professor为非主属性,所以不满足BCNF。修改如下:
Student Table
student_id | p_id |
---|---|
101 | 1 |
101 | 2 |
Professor Table
p_id | professor | subject |
---|---|---|
1 | P.Java | Java |
2 | P.Cpp | C++ |
原文:[实践派]-数据库规范化