前言
之前遇到了别人设计的数据库,一个字段里存储的数据带有集合性质,数据之间用逗号区分,刚开始觉得没什么,操作的时候转换一下就好了,后面发现,做删除的时候就傻眼了。左思右想,觉得这样设计肯定是有问题的,于是翻阅了下数据库的书籍,才恍然拾起遗忘的数据库范式。为什么设计范式,必然是有其道理的,不遵循范式,不说必然,很大程度上,会重蹈软件行业前辈们遇过的坑。
PS:百度百科上都说,傻瓜都不会违反第一范式,我居然还遇到了……
第一范式
所谓第一范式(1NF)是指在关系模型中,对域添加的一个规范要求,所有的域都应该是原子性的,即数据库表的每一列都是不可分割的原子数据项,而不能是集合,数组,记录等非原子数据项。即实体中的某个属性有多个值时,必须拆分为不同的属性。在符合第一范式(1NF)表中的每个域值只能是实体的一个属性或一个属性的一部分。简而言之,第一范式就是无重复的域。
用人话讲就是,字段中属性只能是一个,不能有集合性质。不然的话,在修改和删除操作中,直接让你崩溃。
补充:此外字段中属性也是不开拆分的,不一定说集合才是不可拆分,最好的例子就是地址,如果地址包含省、市、区、详细地址,则是可拆分的。
如果“地址”:”河南省开封市昭阳区balabla”,这样的话是不满足第一范式的。
下表这样处理就是满足第一范式的。
第二范式
首先满足第一范式
第二范式(2NF)要求数据库表中的每个实例或记录必须可以被唯一地区分。选取一个能区分每个实体的属性或属性组,作为实体的唯一标识。如果关系模式R是第一范式的,而且关系中每一个非主属性不部分依赖于主键,称R是第二范式的。
用人话讲第二范式:任意一个字段都只依赖表中的同一个字段 。
偷下懒,引用下别人的例子,这里引用两个例子,帮助理解。
例子1
满足第一范式的前提下,消除部分函数依赖。
StudyNo Name Sex Phone ClassNo ClassAddress 01 john Male kkkk@ee.net 222456 200401 A楼2 01 mary famale kkk@fff.net 123455 200402 A楼3 这个表完全满足于第一范式,
主键由StudyNo和ClassNo组成,这样才能定位到指定行
但是,ClassAddress部分依赖于关键字(ClassNo-〉ClassAddress),
所以要变为两个表表一
StudyNo Name Sex Phone ClassNo 01 john Male kkkk@ee.net 222456 200401 01 mary famale kkk@fff.net 123455 200402 表二
ClassNo ClassAddress 200401 A楼2 200402 A楼3
例子2
学生证 名称 学生证号 学生证办理时间 借书证名称 借书证号 借书证办理时间 改成2张表如下
学生证表 学生证 学生证号 学生证办理时间 借书证表
借书证 借书证号 借书证把你拉时间
第三范式
在1NF基础上,任何非主属性不依赖于其它非主属性[在2NF基础上消除传递依赖]第三范式(3NF)是第二范式(2NF)的一个子集,即满足第三范式(3NF)必须满足第二范式(2NF)。简而言之,第三范式(3NF)要求一个关系中不包含已在其它关系已包含的非主关键字信息。
例如,存在一个部门信息表,其中每个部门有部门编号(dept_id)、部门名称、部门简介等信息。那么在员工信息表中列出部门编号后就不能再将部门名称、部门简介等与部门有关的信息再加入员工信息表中。如果不存在部门信息表,则根据第三范式(3NF)也应该构建它,否则就会有大量的数据冗余。简而言之,第三范式就是属性不依赖于其它非主属性,也就是在满足2NF的基础上,任何非主属性不得传递依赖于主属性。
第三范式是为了消除传递依赖,
传递依赖:设X,Y,Z是关系R中互不相同的属性集合,存在X→Y(Y !→X),Y→Z,则称Z传递函数依赖于X。
例子
不存在非主属性对码的传递性依赖以及部分性依赖
StudyNo Name Sex bounsLevel bouns 20040901 john Male kkkk@ee.net 优秀 $1000 20040902 mary famale kkk@fff.net 良 $600
这个完全满足了第二范式,但是bounsLevel和bouns存在传递依赖
更改为:
StudyNo | Name | Sex | bouunsNo | |
---|---|---|---|---|
20040901 | john | Male | kkkk@ee.net | 1 |
20040902 | mary | famale | kkk@fff.net | 2 |
bounsNo | bounsLevel | bouns |
---|---|---|
1 | 优秀 | $1000 |
2 | 良 | $600 |
这里我比较喜欢用bounsNo作为主键,
基于两个原因
1)不要用字符作为主键。可能有人说:如果我的等级一开始就用数值就代替呢?
2)但是如果等级名称更改了,不叫 1,2 ,3或优、良,这样就可以方便更改,所以我一般优先使用与业务无关的字段作为关键字。
列子2
例:如S1(SNO,SNAME,DNO,DNAME,LOCATION)
各属性分别代表学号, 姓名,所在系,系名称,系地址。
关键字SNO决定各个属性。由于是单个关键字,没有部分依赖的问题,肯定是2NF。但这关系肯定有大量的冗余,有关学生所在的几个属性DNO,DNAME,LOCATION将重复存储,插入,删除和修改时也将产生类似以上例的情况。
原因:
关系中存在传递依赖造成的。
即SNO -> DNO。
而DNO -> SNO却不存在,
DNO -> LOCATION,
因此关键辽 SNO 对 LOCATION 函数决定是通过传递依赖 SNO -> LOCATION 实现的。也就是说,SNO不直接决定非主属性LOCATION。
解决目地:
每个关系模式中不能留有传递依赖。
解决方法:
分为两个关系 S(SNO,SNAME,DNO),D(DNO,DNAME,LOCATION)
注意:关系S中不能没有外关键字DNO。否则两个关系之间失去联系。