一条SQL背后的故事(四)
数据库三大范式
第一范式(1NF)
第一范式强调每一列都是不可分割的原子数据项。
通俗来讲就是表中的某一个字段,不能再拆分成两个或者多个字段。比如 student 表中 班级/班主任 定义为一个字段,这就违背了第一范式。
第二范式(2NF)
数据表里的非主属性都要和这个数据表的候选键有完全依赖关系
这里就需要先说一下数据表中常用的几种键和属性:
- 超键 SUPER KEY:能唯一标识元组的属性集叫做超键。比如用户表中(身份证号)、(身份证号、性别)、(身份证号、姓名)
- 候选键 CANDIDATE KEY:如果超键不包括多余的属性,那么这个超键就是候选键。比如用户表里的身份证号、学生表里的学号、银行卡表里的银行卡号等
- 主键 PRIMARY KEY:用户可以从候选键中选择一个作为主键。
- 外键 FOREIGN KEY:如果数据表R1 中的某属性集不是 R1 的主键,而是另一个数据表 R2 的主键,那么这个属性集就是数据表 R1的外键。比如学生表中的课程编号 或者老师编号都是外键。
- 主属性:包含在任一候选键中的属性称为主属性。
- 非主属性:与主属性相对,指的是不包含在任何一个候选键中的属性。
比如说我们设计一张球员比赛表 player_game,里面包含球员编号、姓名、年龄、比赛编号、比赛时间和比赛场地等属性。
这里候选键和主键都为(球员编号,比赛编号),
我们可以通过候选键来决定如下的关系:
(球员编号, 比赛编号) → (姓名, 年龄, 比赛时间, 比赛场地,得分)
上面这个关系说明球员编号和比赛编号的组合决定了球员的姓名、年龄、比赛时间、比赛地点和该比赛的得分数据。
但是这个数据表不满足第二范式,因为数据表中的字段之间还存在着如下的对应关系:
(球员编号) → (姓名,年龄)
(比赛编号) → (比赛时间, 比赛场地)
不满足第二范式会出现什么问题呢?
1. 数据冗余:如果一个球员可以参加 m 场比赛,那么球员的姓名和年龄就重复了 m-1 次。一个比赛也可能会有 n 个球员参加,比赛的时间和地点就重复了 n-1 次。
2. 插入异常:如果我们想要添加一场新的比赛,但是这时还没有确定参加的球员都有谁,那么就没法插入。
3. 删除异常:如果我要删除某个球员编号,如果没有单独保存比赛表的话,就会同时把比赛信息删除掉。
4. 更新异常:如果我们调整了某个比赛的时间,那么数据表中所有这个比赛的时间都需要进行调整,否则就会出现一场比赛时间不同的情况。
第三范式(3NF)
表中的非主键列必须和主键直接相关而不能间接相关;也就是说:非主键列之间不能相关依赖。
学生表中,学生属于那个班级,班级就可以确定这个班的班主任老师。所以班主任名称就跟学生的学生号存在相关依赖的关系。
学生表:姓名/年龄
eg:张三/11,李四/12,王五/11
姓名,年龄分开就符合第一范式
加个学号 就符合第二范式
加个foreign key 就符合第三范式
第三范式降为第二范式 通过冗余字断提效率性能,就是反范式设计。