数据库三大范式
第一范式(1NF):
是指数据库表的每一列都是不可分割的基本数据项,同一列中不能有多个值,即实体中的某个属性不能有多个值或者不能有重复的属性。个人总结:不能有表中表。
如数据表不能这样设计,因为数据表的一个二维表:
姓名 | 思想道德素质测评(20%) | 专业素质测评(60%) | |||||||||
基础分 | 奖励分 | 惩罚分 | 最后得分 | 排名 | 必修课成绩 | 奖励分 | 惩罚分 | 最后得分 | 排名 | ||
得分 | 排名 | ||||||||||
张三 | 78 | 0 | 0 | 78 | 2 | 71.7 | 1 | 0 | 0.0 | 71.7 | 1 |
王五 | 78 | 0 | 0 | 78 | 2 | 71.3 | 24 | 0.0 | 0.0 | 71.3 | 2 |
李四 | 78 | 0 | 0 | 78 | 2 | 70.8 | 25 | 0.0 | 2.0 | 68.8 | 3 |
而这样设计是可以的:
姓名 | C语言 | java | MySQL | Oracle |
张三 | 78 | 88 | 68 | 78 |
王五 | 78 | 55 | 87 | 78 |
李四 | 78 | 86 | 91 | 56 |
第二范式(2NF):
要求数据库表中的每个实例或行必须可以被惟一地区分,数据库表中不存在非关键字段对任一候选关键字段的部分函数依赖(部分函数依赖指的是存在组合关键字中的某些字段决定非关键字段的情况),也即所有非关键字段都完全依赖于任意一组候选关键字。个人总结:非主属性依赖于主属性
如以下成绩表
student_id | course_id | credit(学分 | score age |
20101661 | 1 | 2 | 80 20 |
20101662 | 3 | 2 | 90 22 |
student(student_id, age);
course_student(student_id, course_id, score);
course(course_id, credit)
会出现如下情况:
(1) 数据冗余:
同一门课程由n个学生选修,"学分"就重复n-1次;同一个学生选修了m门课程,姓名和年龄就重复了m-1次。
(2) 更新异常:
若调整了某门课程的学分,数据表中所有行的"学分"值都要更新,否则会出现同一门课程学分不同的情况。
(3) 插入异常:
假设要开设一门新的课程,暂时还没有人选修。这样,由于还没有"学号"关键字,课程名称和学分也无法记录入数据库。
(4) 删除异常:
假设一批学生已经完成课程的选修,这些选修记录就应该从数据库表中删除。但是,与此同时,课程名称和学分信息也被删除了。很显然,这也会导致插入异常。
第三范式(3NF):
要求一个数据库表中不包含已在其它表中已包含的非主关键字信息,在第二范式的基础上,数据表中如果不存在非关键字段对任一候选关键字段的传递函数依赖则符合第三范式。 个人总结:不能有传递性
如以下表
student_id | course_id | birthday | SD(所在系的名称) | SL(所在宿舍楼) |
20101661 | 1 | 1991,07,14 | 软件学院 | 1栋 |
20101662 | 3 | 1993,7,25 | 经管学院 | 2栋 |
student_id为主码,SL依赖于student_id,但可以从非主码属性SD导出,即知道了SD,也就知道了SL。可以奖这个表拆分成两个表,如下:
grade(student_id, course_id,SD);
department(SD, SL);
SD为联系grade表的主键
不符合第三范式,也会出现类似数据冗余、更新异常、插入异常和删除异常
注意:
1:从第二范式和第三范式(一对多),好像有点类似,都是非主属性可以得到另外一个非主属性,但是最主要的区别是第二范式是建立在联合主键(多对多)的情况下(虽然设计在不会出现联合主键(有单独的id),但是我们还是会认为是联合主键)。
2:有的时候,为了查询效率,会添加一些冗余字段在表中,冗余字段一般是不经常更改的数据。
例:有表t_bids(id, user_id),t_users(id, name),t_invests(id, bid_id, user_id)
表t_invests就不符合第三范式,因为通过bid_id,可以查询到user_id,这个相当于user_id就是冗余字段(而user_id又不会经常改变),设置这个冗余字段也有其好处。
现在要大量查询t_invests表中用户的姓名,这时如果不设置user_id,就要进行三表联查。