数据库范式之第二范式

第二范式(2NF)

第一范式的不足

昨天说到了数据库的第一范式,但是如果仅仅符合1NF的设计,仍然会存在数据冗余过大,插入异常,删除异常,修改异常的问题,例如下面学生表的设计:

学生表

1、每一名学生的学号、姓名、系名、系主任这些数据重复多次。每个系与对应的系主任的数据也重复多次——数据冗余过大

2、假如学校新建了一个系,但是暂时还没有招收任何学生(比如3月份就新建了,但要等到8月份才招生),那么是无法将系名与系主任的数据单独地添加到数据表中去的 (注1)——插入异常

注1:根据三种关系完整性约束中实体完整性的要求,关系中的码(注2)所包含的任意一个属性都不能为空,所有属性的组合也不能重复。为了满足此要求,图中的表,只能将学号与课名的组合作为码,否则就无法唯一地区分每一条记录。

注2:码:关系中的某个属性或者某几个属性的组合,用于区分每个元组(可以把“元组”理解为一张表中的每条记录,也就是每一行)。

3、假如将某个系中所有学生相关的记录都删除,那么所有系与系主任的数据也就随之消失了(一个系所有学生都没有了,并不表示这个系就没有了)。——删除异常

4、假如李小明转系到法律系,那么为了保证数据库中数据的一致性,需要修改三条记录中系与系主任的数据。——修改异常

正因为仅符合1NF的数据库设计存在着这样那样的问题,我们需要提高设计标准,去掉导致上述四种问题的因素,使其符合更高一级的范式(2NF),这就是所谓的“规范化”。

第二范式举例

第二范式的规则是要求数据表里的所有数据都要和该数据表的主键有完全依赖关系;如果有哪些数据只和主键的一部分有关的话,就得把它们独立出来变成另一个数据表。如果一个数据表的主键只有单一一个字段的话,它就一定符合第二范式。

下面举出一个例子:

组件ID(主键) 价格 供应商ID(主键) 供应商名称 供应商住址
65 59.99 1 Nick VA
73 20.00 1 Nick VA
65 69.99 2 Pick CA

这个数据表的每个值都是单一值,所以它符合第一范式。因为同一个组件有可能由不同的供应商提供,所以得把组件 ID 和供应商 ID 合在一起组成一个主键。

组件(关键词)和价格之间的关系很正确:同一个组件在不同供应商有可能会有不同的报价,所以价格确实和主键完全相关(完全依赖)。

另一方面,供应商的名称和住址就只和供应商 ID 有关(部分依赖),这不符合第二范式的原则。仔细看就会发现 “Nick” 这个名称和 “VA” 这个住址重复出现了两次;要是它改名了或是被其他公司并购了怎么办?这时候最好把这些数据存到第二个数据表中:

供应商ID(主键) 供应商名称 供应商住址
1 Nick VA
2 Pick CA

这么一来,原本的 “组件来源” 数据表就得要做相对应的改动:

组件ID(主键) 价格 供应商ID(外键)
65 59.99 1
73 20.00 1
65 69.99 2

检查数据表里的每个字段,确认它们是不是都和关键词完全相关, 这样才能知道这个数据表是不是符合第二范式; 如果不是的话,就把那些不完全相关的字段移到独立的数据表里。 接下来的步骤是要确保所有不是键的字段都和彼此没有相依关系,这就叫做第三范式。

第二范式(2NF)在关系理论中的严格定义我这里就不多介绍了(因为涉及到的铺垫比较多),只需要了解2NF对1NF进行了哪些改进即可。其改进是,2NF在1NF的基础之上,消除了非主属性对于码的部分函数依赖

函数依赖

函数依赖简单点说就是:某个属性集决定另一个属性集时,称另一属性集依赖于该属性集。

设R(U)是一个属性集U上的关系模式,X和Y是U的子集。

若对于R(U)的任意两个可能的关系r1、r2,若r1[x]=r2[x],则r1[y]=r2[y],或者若r1[x]不等于r2[x],则r1[y]不等于r2[y],称X决定Y,或者Y依赖X。

上面一段话是某些教材上的话,比较不好理解。比如在设计学生表时,一个学生的学号能决定学生的姓名,也可称姓名属性依赖于学号,对于现实来说,就是如果知道一个学生的学号,就一定能知道学生的姓名,这种情况就是姓名依赖于学号,这就是函数依赖,函数依赖又分为非平凡依赖,平凡依赖;从性质上还可以分为完全函数依赖、部分函数依赖和传递函数依赖。

例如对于函数Y=f(X)来说,

  1. 数据依赖
    在计算机科学中,数据依赖是指一种状态,当程序结构导致数据引用之前处理过的数据时的状态。其中最重要的是函数依赖和多值依赖。
  2. 函数依赖
    设X,Y是关系R的两个属性集合,当任何时刻R中的任意两个元组中的X属性值相同时,则它们的Y属性值也相同,则称X函数决定Y,或Y函数依赖于X。
  3. 平凡函数依赖
    当关系中属性集合Y是属性集合X的子集时(Y?X),存在函数依赖X→Y,即一组属性函数决定它的所有子集,这种函数依赖称为平凡函数依赖。
  4. 非平凡函数依赖
    当关系中属性集合Y不是属性集合X的子集时,存在函数依赖X→Y,则称这种函数依赖为非平凡函数依赖。
  5. 完全函数依赖
    设X,Y是关系R的两个属性集合,X’是X的真子集,存在X→Y,但对每一个X’都有X’!→Y,则称Y完全函数依赖于X。
  6. 部分函数依赖
    设X,Y是关系R的两个属性集合,存在X→Y,若X’是X的真子集,存在X’→Y,则称Y部分函数依赖于X。
  7. 传递函数依赖
    设X,Y,Z是关系R中互不相同的属性集合,存在X→Y(Y !→X),Y→Z,则称Z传递函数依赖于X。

函数依赖和属性的关系

属性之间有三种关系,但并不是每一种关系都存在函数依赖。设R(U)是属性集U上的关系模式,X、Y是U的子集:

  • 如果X和Y之间是1:1关系(一对一关系),如学校和校长之间就是1:1关系,则存在函数依赖X → Y和Y →X。
  • 如果X和Y之间是1:n关系(一对多关系),如年龄和姓名之间就是1:n关系,则存在函数依赖Y → X。
  • 如果X和Y之间是m:n关系(多对多关系),如学生和课程之间就是m:n关系,则X和Y之间不存在函数依赖。

参考资料: