详细讲解数据库第一、第二、第三范式

时间:2024-03-23 22:17:21

什么是数据库范式

在设计关系数据库的时候,要求遵循不同的规范,而这些规范就被称为范式。

有如下的几种范式:

第一范式(1NF):在关系数据库中,每张表的属性应该具有原子性。

第二范式(2NF):在1NF基础上消除非主属性对主码的部分函数依赖,非码属性必须完全依赖于候选码。

第三范式(3NF):在2NF基础上消除传递依赖,任何非主属性不依赖于其它非主属性。

巴斯-科德范式(BCNF):在3NF基础上消除对主码子集的依赖,任何非主属性不能对主键子集依赖。

第四范式 (4NF):在BCNF基础上消去其中不是函数依赖的非平多值依赖。

第五范式(5NF):在4NF基础上,表必须可以分解为较小的表,除非那些表在逻辑上拥有与原始表相同的主键。

可以看出范式是呈现递次规范的,而越高的规范数据库的冗余就越小。

在解释范式之前,有必要先了解一些概念:

候选码/候选键(候选关键字)、主码/主键(主关键字)、主属性、非主属性。

在后面统一用码来描述,首先一张表肯定要存在有码这种关系,码在最上面已经给出了解释。

**码/超键:**数据表中关系的某个属性或者某几个属性的组合,用于区分每个元组。

**元组:**表中的每行记录。

码、候选码、主码三者关系:

图:

详细讲解数据库第一、第二、第三范式

候选码:唯一标识一条记录的最小属性集。

  • 标识性:一个数据表的所有记录都具有不同的候选键
  • 最小性:候选键的任何子集都不能唯一标识一个记录
  • 非空性:不能为空
  • 候选键是没有多余属性的超键。

例如学号是候选码,包含有候选码的属性集都是码。

主码:某个能够唯一标识一条记录的最小属性集(从候选码挑选的一条)。

  • 唯一性:一个数据表只能有一个主键
  • 标识性:一个数据表的所有记录都具有不同的主键取值
  • 非空性:不能为空
  • 人为的选取某个候选码为主码

**外键:**子数据表中出现的父数据表的主键。

例如:

详细讲解数据库第一、第二、第三范式

班主任也有编号,在教师表中,但是班主任编号在学生表中就是属于外键。

主属性:候选码属性的并集。

非主属性:相对于主属性来说,不包含在候选码中的属性。

函数依赖:

例如:在学生信息表中,不允许重名,如果重名就不是函数依赖关系了。

详细讲解数据库第一、第二、第三范式

在这张表中学号就代表了学生,如果有两个名字相同的,例如两个小明,那么1001就不能代表小明这一属性了,也就是说姓名不能够依赖于学号这一属性了。如果不允许重名,那么就可以说学生姓名函数依赖于学号,记作学号—>姓名。

表中还有这些依赖关系:

学号—>性别

学号—>年龄

姓名—>性别

姓名—>年龄

如果表中多了一个属性课名,则学号—>课名就不成立了,因为指不定一个人有很多的课程。

完全函数依赖:

在上面表中加入班级属性。

详细讲解数据库第一、第二、第三范式

假设不同的班级学号有相同的,班级内学号不相同,在表中(学号、班级)—> 姓名 ,

但是学号—>姓名,班级—>姓名 不成立,所以在这张表中姓名完全函数依赖于(学号、班级)。

部分依赖关系:

详细讲解数据库第一、第二、第三范式

假设表中学号属性值是唯一的,在表关系中存在:(学号,身份证号)->(姓名),(学号)->(姓名),(身份证号)->(姓名),则可以说姓名部分函数依赖于(学号,身份证号)。

传递函数依赖:

详细讲解数据库第一、第二、第三范式

在表关系中,姓名函数依赖于宿舍,但是反过来就不行,因为指不定宿舍不止一个人,同样宿舍函数依赖于费用,反过来不行,因为不知道每个人的费用情况为多少,而费用又函数依赖于姓名,所以在这个表关系中费用传递函数依赖于姓名。

最小函数依赖:

详细讲解数据库第一、第二、第三范式

假设不同的班级学号有相同的,班级内学号不相同,则姓名完全函数依赖于(学号、班级)记为 (学号、班级)—> 姓名,而姓名为单属性的,此时就的(学号、班级)—> 姓名中间没有传递函数依赖层,所以可以说(学号、班级)—> 姓名就为表关系的最小函数依赖。

例如 班级—>班长,班长—>班主任,班级—>班主任,其中班级—>班主任在表关系中就不是最小函数依赖了,因为班级—>班长,班长—>班主任,就代表着班级—>班主任。

而这些最小函数依赖组成的集合就是最小函数依赖集。

平凡函数依赖:

详细讲解数据库第一、第二、第三范式

假设不同的班级学号有相同的,班级内学号不相同,在表关系中存在(学号,班级,姓名)函数依赖于姓名,而姓名属性也包含在(学号,班级,姓名)这个属性集里面,此时就能说上一组依赖为平凡函数依赖。

非平凡函数依赖:

详细讲解数据库第一、第二、第三范式

假设不同的班级学号有相同的,班级内学号不相同,在表关系中存在(学号,姓名,班级)—>(班长,班主任),此时的(学号,姓名,班级)这个集不包含(班长,班主任)这个属性集,则可以称函数依赖为非平凡函数依赖。


1NF

根据第一范式的关系模型要求,数据表的每一列都具有原子性,也就是每一列都不可分隔出来。

像这样就不行,这样不符合关系数据库的设计要求:

详细讲解数据库第一、第二、第三范式

首先来看看仅仅是符合第一范式的数据表所呈现的问题:

详细讲解数据库第一、第二、第三范式

虽然只是简单的满足了1NF,但是还是会存在数据冗余过大,插入、删除、修改异常的问题。

表中每个字段数据都出现了多次的重复,例如小王与大王关联任务数据出现重复,数据的冗余过大。

问题:

插入异常

如果当人们有了夜宵的习惯,那么数据库表就要加入该字段,而目前的小王与大王都没有夜宵的习惯,那么该表就无法单独为夜宵这一任务进行添加了。

问题解决:

受表中关系完整性的约束,每个关系组合都不能为空,也就是码不能为空,且属性的组合不能重复,

所以只能将实体小王与大王和其他属性组合分开即:

详细讲解数据库第一、第二、第三范式

删除异常

在上面的1NF问题图中如果单单的删除一个实体对象的整行记录(1)的话,那么与之组合的其他属性就会随之消失,这样其他的人例如大王就没有早晨吃了,就算小王大王不吃早餐,如果其他人要吃,准备进来怎么办,发现没有早餐吃,不就异常了。

修改异常

如果小王比较特殊,经常熬夜通宵,夜晚正是他最精神的时刻,晚上18:30才吃早餐,所以他把数据改了一下,但是数据库中数据是具有一致性的,这时候其他人/大王不干了,本来晚餐应该大吃一顿的,却让我只能吃面包油条,罢工。

2NF

那么2NF是怎么解决这样的问题的呢?

在1NF基础上消除非主属性对主码的部分函数依赖,非码属性必须完全依赖于候选码。

为了能够更严谨的解决问题,后面将改为另一张比较经典的表。

详细讲解数据库第一、第二、第三范式

如何判断表中是否符合2NF呢?

首先找出该表的所有码:(学号、课名),所以主属性为学号与课名,自然非主属性为姓名,系名,系主任,分数了。而又该表中存在非主属性对码的部分函数依赖,例如:学号—>姓名等等。所以很明显是不符合2NF的。

怎么改进呢?就行分解,这里这样修改:

选课表:(学号,课名,分数)
学生表:(学号,姓名,系名,系主任)

再来看看两张表的关系:

选课表中的码:(学号,课名),所以主属性为学号、课名,非主属性为分数。

该表中如果学号确定,并不能唯一确定分数课名确定,也不能唯一确定分数,所以不存在非主属性分数对于码**(学号,课名)**的部分函数依赖,所以此表符合2NF的要求。

学生表中的码:学号,所以主属性为学号,非主属性为姓名、系名、系主任,因为码只有一个属性,所以不会存在非主属性对码的部分函数依赖,所以该表也符合2NF的要求。

来看看两张表中是否存在1NF中的删除,插入,修改异常。

删除:

如果删除一个系的话,则系的全部信息会丢失了。

插入:

还是不能直接插入一个系。

修改:

如果小明转系了,直接修改即可。

可以发现,将1NF中的问题表分解成两个表,解决了属性值修改异常的问题,也的确减少了数据冗余情况,但是还是存在删除与插入的异常。

而3NF又是怎么去解决这个问题呢?


3NF

在2NF的基础之上,消除了非主属性对码的传递函数依赖,如果存在该关系,则不符合3NF。

回到2NF中的两个表:

选课表:(学号,课名,分数)
学生表:(学号,姓名,系名,系主任)

选课表:由于非主属性只有一个,所以不可能存在传递函数依赖,所以符合。

学生表:存在学号—>系名,系名—>系主任,学号—>系主任,存在传递函数依赖,所以不符合。

怎么解决呢?对出现问题的学生表就行分解:

分解成:

学生表:(学号,姓名,系名)

系表:(系名,系主任)

分析这两个表:

学生表:非主属性只有系名一个,不会存在传递函数依赖,所以符合。

系表:非主属性只有一个,同样不符合,且表中只有两个属性,至少三个或者以上才可能会出现传递函数依赖。

再来看看2NF中留下的问题能不能解决?

删除:删除某个系的学生,系信息不会丢失,成功。

插入:直接插入,无影响,成功。

在平常的数据库表设计中,必须懂得第一二三范式,不然表数据冗余过大就不好了。

而实际的设计中,又非得必须遵守这三条铁律,依情况而定。