1、兵马未动,粮草先行
要设计出合理的关系型数据库,就需要遵循一些规范。这些不同的规范要求被称为不同的范式,各种范式呈递次规范,要遵循后面的规范先要遵循前面的规范,越高的范式数据库冗余越小。这里只讨论前三种范式,分别是第一范式(1NF)、第二范式(2NF)和第三范式(3NF)。
NF是normal form 的缩写。
码:如果在一张表中,一个属性或属性组,被其他所有属性所完全依赖,则称这个属性(或属性组)为该表的码(也就是通常所说的键)。
函数依赖:A—>B,如果根据B属性(或属性组)的值,可以唯一确定A属性的值。则称A依赖于B。例如:学生name—> 学生id,即根据学生id可以唯一确定学生姓名;课程score—> (学生id,课程id),即根据学生id和课程id可以唯一确定某学生的某课程的分数。
完全函数依赖:A—>B, 如果B是一个属性组,则A属性值的确定需要依赖于B属性组中所有的属性值。例如:课程score—> (学生id,课程id),即只有学生id和课程id都确定了,才能唯一确定某学生的某课程的分数,缺一不可。
部分函数依赖:A—>B, 如果B是一个属性组,则A属性值得确定只需要依赖于B属性组中某一些值即可。例如:学生name—> (学生id,课程id),即只需要学生id就可以唯一确定学生姓名。
传递函数依赖:A—>B, B —>C . 如果通过B属性(或属性组)的值,可以唯一确定A属性的值,在通过C属性(属性组)的值可以确定唯一B属性的值,则称 A 传递函数依赖于C。例如:院系id—> 学生id,院系name—> 院系id。即根据学生id可以确定其所在院系id,再根据院系id可以确定院系名称,也就是根据学生id可以唯一确定其所在院系名称。
2、排兵布阵
探马来报,敌方来势汹汹信息如下。
表1
学生id | 学生姓名 | 院系信息 | 课程信息 |
---|---|---|---|
10001 | 郭靖 | 1,机械系 | 101,计算机基础,60分 |
10001 | 郭靖 | 1,机械系 | 102,大学数学,60分 |
10002 | 黄蓉 | 2,经管系 | 101,计算机基础,80分 |
10002 | 黄蓉 | 2,经管系 | 102,大学数学,99分 |
10003 | 杨过 | 3,外语系 | 101,计算机基础,80分 |
10003 | 杨过 | 3,外语系 | 102,大学数学,90分 |
10004 | 小龙女 | 4,艺术系 | 101,计算机基础,99分 |
10004 | 小龙女 | 4,艺术系 | 102,大学数学,70分 |
10005 | 令狐冲 | 4,艺术系 | 101,计算机基础,50分 |
10005 | 令狐冲 | 4,艺术系 | 102,大学数学,50分 |
羽扇纶巾,休要惊慌。
第一阵:名叫第一范式(1NF),即每一列都是不可再分割的原子数据项。院系信息和课程信息都不符合。
表2
学生id | 学生姓名 | 院系id | 院系名称 | 课程id | 课程名称 | 课程分数 |
---|---|---|---|---|---|---|
10001 | 郭靖 | 1 | 机械系 | 101 | 计算机基础 | 60 |
10001 | 郭靖 | 1 | 机械系 | 102 | 大学数学 | 60 |
10002 | 黄蓉 | 2 | 经管系 | 101 | 计算机基础 | 80 |
10002 | 黄蓉 | 2 | 经管系 | 102 | 大学数学 | 99 |
10003 | 杨过 | 3 | 外语系 | 101 | 计算机基础 | 80 |
10003 | 杨过 | 3 | 外语系 | 102 | 大学数学 | 90 |
10004 | 小龙女 | 4 | 艺术系 | 101 | 计算机基础 | 99 |
10004 | 小龙女 | 4 | 艺术系 | 102 | 大学数学 | 70 |
10005 | 令狐冲 | 4 | 艺术系 | 101 | 计算机基础 | 50 |
10005 | 令狐冲 | 4 | 艺术系 | 102 | 大学数学 | 50 |
第二阵:名叫第二范式(2NF),即非码属性必须完全依赖于码(消除非主键对主键的部分函数依赖)。表2的码为属性组(学生id,课程id),其中课程score完全依赖于这个属性组,但学生name和院系name并不完全依赖于它。所以分拆如下表3和表4。
表3
学生id | 课程id | 课程名称 | 课程分数 |
---|---|---|---|
10001 | 101 | 计算机基础 | 60 |
10001 | 102 | 大学数学 | 60 |
10002 | 101 | 计算机基础 | 80 |
10002 | 102 | 大学数学 | 99 |
10003 | 101 | 计算机基础 | 80 |
10003 | 102 | 大学数学 | 90 |
10004 | 101 | 计算机基础 | 99 |
10004 | 102 | 大学数学 | 70 |
10005 | 101 | 计算机基础 | 99 |
10005 | 102 | 大学数学 | 70 |
表4
学生id | 学生姓名 | 院系id | 院系名称 |
---|---|---|---|
10001 | 郭靖 | 1 | 机械系 |
10002 | 黄蓉 | 2 | 经管系 |
10003 | 杨过 | 3 | 外语系 |
10004 | 小龙女 | 4 | 艺术系 |
10005 | 令狐冲 | 4 | 艺术系 |
对表3再次使用第二范式,课程score完全依赖于属性组(学生id和课程id),但课程name并不完全依赖于它。所以分拆如下表5和表6。
表5 学生课程分数表
学生id | 课程id | 课程分数 |
---|---|---|
10001 | 101 | 60 |
10001 | 102 | 60 |
10002 | 101 | 80 |
10002 | 102 | 99 |
10003 | 101 | 80 |
10003 | 102 | 90 |
10004 | 101 | 99 |
10004 | 102 | 70 |
10005 | 101 | 50 |
10005 | 102 | 50 |
表6 课程表
课程id | 课程名称 |
---|---|
101 | 计算机基础 |
102 | 大学数学 |
第三阵:叫做第三范式,即任何非主属性不依赖于其它非主属性(消除传递依赖)。看表4,这里学生id是主属性。院系id依赖于学生id,院系名称是一个非主属性,院系id也是一个非主属性,而院系名称又依赖于院系id 。所以,表4拆分为如下表7和表8。
表7 学生表
学生id | 学生姓名 | 院系id |
---|---|---|
10001 | 郭靖 | 1 |
10002 | 黄蓉 | 2 |
10003 | 杨过 | 3 |
10004 | 小龙女 | 4 |
10005 | 令狐冲 | 4 |
表8 院系表
院系id | 院系名称 |
---|---|
1 | 机械系 |
2 | 经管系 |
3 | 外语系 |
4 | 艺术系 |
至此,表1已被完全分解为表5、表6、表7和表8,完全符合三大范式,是设计的比较合理的表。
3、打扫战场
表1被分解为表5、表6、表7和表8的过程,是符合三大范式的,其总的思想无外乎明确一件事,即每个表都只保存一类事物,这正符合了数据库表和Java中的实体类的ORM(Object Relational Mapping 对象关系映射)思想。
表1的形式结果可能更多用于在应用客户端展示信息时使用。
仍要注意,按照三大范式设计表理论上是可行的,这也大大减少了数据库中数据的冗余。但切不可固步自封,在实际开发业务场景中,有时候为了方便信息的获取与展示,有时可能还需要故意增加个别冗余字段来达到目的。因此,最终的表结构仍要根据具体问题具体分析具体设计。