通常来说,随便有个数据库库就可以编程。而通常来说,虽然做得很痛苦,任何一个程序员都可以在很烂的数据库设计的基础上找到解决业务问题的方法,唯一不一样的是痛苦程度。最后有一句话很有道理,就是:如果不知道为什么活着,为什么工作,那就先拼命赚钱!
哈哈~如果不知道为什么需要最大限度遵守三范式,那就先最大限度遵守先…
为什么需要符合三范式
其实在现在的很多场景下的确有很多破坏范式的情况,譬如数据仓库系统中,满足二范式就比较理想的设计了得,因为有很多牺牲控件换性能的情况。又如lucene的应用,就会故意把数据平铺成一条记录,方便搜索。但是对于业务系统而言,遵守三范式是非常有必要的,即使你暂时不知道有什么好处,先遵守就会让你的功能拥有更多的扩展能力。
不过不落下这个问题不解决,网络上的文章描述得很好,符合三范式的目的就是为了避免:数据冗余、更新异常、插入异常、删除异常、查询异常。
第一范式(1NF)
所谓第一范式(1NF)是指数据库表的每一列都是不可分割的基本数据项,同一列中不能有多个值。要分两个方向去理解,把业务实体中本身不需要分割的字段信息故意拆解为两个字段存储,或者把业务实体中本身需要两个字段存储的信息合并到一起存储,这两种做法都有问题的。
比如员工表,不能把性别、年龄和电话号码存储到一个字段上。
上面提到的问题怎么体现,就不多描述,反正没有人这么干。
第二范式(2NF)
第二范式(2NF)是在第一范式(1NF)的基础上建立起来的,要求数据库表中不存在非关键字段对任一候选关键字段的部分函数依赖。(部分函数依赖指的是存在组合关键字中的某些字段决定非关键字段的情况),也即所有非关键字段都完全依赖于任意一组候选关键字。
概念很难理解,可以这么理解:
- 表的每一行必须能找到业务相关的唯一主键,可以由多个字段组成。当然,非业务主键也是必须的。
- 表中每一行,对于业务主键中的任意一个或者多个字段,但不是全部字段,不能是同一行记录中其他非业务字段的主键。
还不好理解,看例子:
假定选课关系表为SelectCourse(学号, 姓名, 年龄, 课程名称, 成绩, 学分),业务主键为组合字段(学号, 课程名称),因为存在如下决定关系:
(学号, 课程名称) → (姓名, 年龄, 成绩, 学分)
这个数据库表不满足第二范式,因为存在如下依赖关系:
(课程名称) → (学分)
(学号) → (姓名, 年龄)
即存在组合关键字中的字段决定非关键字的情况。
由于不符合2NF,这个选课关系表会存在如下问题:
(1) 数据冗余:
同一门课程由n个学生选修,"学分"就重复n-1次;同一个学生选修了m门课程,姓名和年龄就重复了m-1次。
(2) 更新异常:
若调整了某门课程的学分,数据表中所有行的"学分"值都要更新,否则会出现同一门课程学分不同的情况。
(3) 插入异常:
假设要开设一门新的课程,暂时还没有人选修。这样,由于还没有"学号"关键字,课程名称和学分也无法记录入数据库。
(4) 删除异常:
假设一批学生已经完成课程的选修,这些选修记录就应该从数据库表中删除。但是,与此同时,课程名称和学分信息也被删除了。很显然,这也会导致插入异常。
(5)查询异常:不写了...
把选课关系表SelectCourse改为如下三个表:
学生:Student(学号, 姓名, 年龄);
课程:Course(课程名称, 学分);
选课关系:SelectCourse(学号, 课程名称, 成绩)。
第三范式(3NF)
在第二范式的基础上,数据表中如果不存在非关键字段对任一候选关键字段的传递函数依赖则符合第三范式。
可以这么理解:
1)表的每一行有合理的业务主键,同第二范式中描述的。
2)业务主键唯一确定了字段C、但是字段C由唯一确定字段E。
同样看看例子:
假定学生关系表为Student(学号, 姓名, 年龄, 所在学院, 学院地点, 学院电话),关键字为单一关键字”学号”,因为存在如下决定关系:
(学号) → (姓名, 年龄, 所在学院, 学院地点, 学院电话)
这个数据库是符合2NF的,但是不符合3NF,因为存在如下决定关系:
(学号) → (所在学院) → (学院地点, 学院电话)
即存在非关键字段”学院地点”、”学院电话”对关键字段”学号”的传递函数依赖。
它也会存在数据冗余、更新异常、插入异常和删除异常的情况,读者可自行分析得知。
把学生关系表分为如下两个表:
学生:(学号, 姓名, 年龄, 所在学院);
学院:(学院, 地点, 电话)。
如果不这么做的话,影响还是那5个问题。