目录
数据库的设计范式是数据库设计所需要满足的规范,满足这些规范的数据库是简洁的、结构明晰的,同时,不会发生插入(insert)、删除(delete)和更新(update)操作异常。
单纯通过概念原理性的描述来了解三大范式可能会一头雾水,像我在一开始接触到数据库三范式的时候也是分不清第二范式和第三范式的关系,后来记住了概念一段时间不使用就描述不清楚了,今天有人问起,于是我想出了一个例子来具体说下数据库三大范式。
如下表,是不符合数据库三大范式的,下面通过数据库三大范式依次对数据表进行改造:
字段解释:
student_id | 学号 |
student_name | 学生姓名 |
course | 课程 |
score | 得分 |
class | 班级 |
headteacher | 班主任 |
第一范式(1NF):
每一列属性都是不可再分的属性值,是原子化、不可再分解的,1NF就是确保每一列的原子性。因此,两列的属性相近或相似或一样(描述的主体是同一个对象的多个属性),尽量合并属性一样的列,确保不产生冗余数据。
很明显,表中的course列,包含两个属性:课程编号和课程名称,不符合数据库的第一范式,即不符合原子性,所以要将这一列拆分成两列:
这样,这张表就符合数据库的第一范式了。
第二范式(2NF)
2NF是在1NF的基础上建立起来的,即满足2NF必须先满足1NF。2NF要求所有属性要完全依赖于主键,是对记录的唯一性约束,要求记录有唯一标识,即实体的唯一性;为实现区分通常需要为表设置一个或多个属性值,即主键,多个属性值就是联合主键以存储各个实例的惟一标识。这个唯一属性列被称为主键。
可以看到,student_id、course_id作为复合主键,要保证其他非主键元素要依赖主键元素,也就是说所有的列都要同时依赖于student_id、course_id这两个属性。但是在表中,student_name,class,headteacher只依赖于student_id,course_name只依赖于course_id,所以这两个_name属性就不符合2NF,要将这些依赖关系独立成表:
学生信息表:
课程信息表:
学生得分表:
第三范式(3NF)
属性不依赖于其它非主属性 属性直接依赖于主键,3NF是对字段冗余性的约束,即任何字段不能由其他字段派生出来,它要求字段没有冗余。数据不能存在传递关系,即每个属性都跟主键有直接关系而不是间接关系。像:a-->b-->c 属性之间含有这样的关系,是不符合第三范式的。
我们注意到,在按照2NF设计的学生信息表中,headteacher属性值依赖于class属性值,也就是属性依赖于非主属性值:
学生表:
按照3NF改进:
学生表:
班级表:
再比如Student表(学号,姓名,年龄,性别,所在院校,院校地址,院校电话),就存在上述关系。 学号--> 所在院校 --> (院校地址,院校电话),按照3NF改进:学生表(学号,姓名,年龄,性别,所在院校)院校表(所在院校,院校地址,院校电话)。
范式化设计 | 反范式化设计 | |
优点 | 可以尽量减少数据冗余,使得更新快,体积小。 | 可以减少表的关联,可以更好得进行索引优化 |
缺点 | 对于查询需要多个表进行关联,减少写得效率增加读得效率,更难进行索引优化 | 数据冗余以及数据异常,数据得修改需要更多的成本。 |
总结:三大范式只是一般设计数据库的基本理念,可以建立冗余较小、结构合理的数据库。如果有特殊情况,当然要特殊对待,数据库设计最重要的是看需求跟性能,需求>性能>表结构。所以不能一味的去追求范式建立数据库。
复合主键
指表的主键含有一个以上的字段组成,不使用无业务含义的自增id作为主键。比如name和id字段组合起来就是表的复合主键 ,它的出现是因为你的name字段可能会出现重名,所以要加上ID字段这样就可以保证你记录的唯一性 ,一般情况下,主键的字段长度和字段数目要越少越好 。
关于“主键是唯一的索引”
这句话是有局限性的,当我们在表中创建了一个ID字段,自增,并设为主键,因为ID自动增长保证了唯一性,所以这种情况下符合“主键是唯一的索引”。此时,在当前表中再创建一个字段name,类型为varchar,也设置为主键,你会发现,在表的多行中你是可以填写相同的name值的,这岂不是有违“主键是唯一的索引”这句话么,所以“主键是唯一的索引”是有歧义的。实际上应该是“当表中只有一个主键时,它是唯一的索引。当表中有多个主键时,称为复合主键,复合主键联合保证唯一索引”。
既然自增长ID已经可以作为唯一标识的主键,为什么还需要复合主键呢。因为并不是所有的表都要有ID这个字段,比如,我们建一个学生表,没有唯一能标识学生的ID,这种情况下学生的名字、年龄、班级都可能重复,无法使用单个字段来唯一标识,这时可以将多个字段设置为主键,形成复合主键,这多个字段联合标识唯一性,其中,某几个主键字段值出现重复是没有问题的,只要不是有多条记录的所有主键值完全一样,就不算重复。
联合主键
联合主键指的是多张表的主键结合在一张中间表中,方便连查多张表的信息,为了查询两个表之间的元素,我们创建一个中间表,然后就可以通过中间表进行连表查询操作,其中中间表的id即为联合主键。联合主键是用2个字段(或者多个字段,后面具体都是用2个字段组合)来确定一条记录,说明这2个字段都不是唯一的,2个字段可以分别重复,这么设置可以很直观地看到某个重复字段的记录条数。
比如主键A跟主键B组成联合主键,主键A跟主键B的数据可以相同也可以分别重复,联合就在于主键A跟主键B形成的联合主键是唯一的。比如主键A数据是1,主键B数据也是1,联合主键其实是11,这个11是唯一值,绝对不充许再出现11这个唯一值。(这就是多对多关系)
总结: 复合主键就是含有一个以上的字段组成,如ID+name,ID+phone等,而联合主键要同时是两个表(或多个表)的主键组合起来的。