关系型数据库可以看做“有多个表,表内的数据之间、表与表之间存在关系”的数据库,每个表都由行列组成,列又称作字段、域(Field),行又称作记录(Record),一张表的列结构称为表结构,也叫模式(Schema)。
在面向对象的思想指导下,遵循模式建立的表称为模型(Model),具体的记录称为实体(Entity),表字段称为字段,具体的记录的某个字段称为属性(Property)。
抽象和具体的区别,就像员工表有“性别”字段,而员工“甲”有性别“男”这个属性。
有些概念之间经常被混用,不同人对不同概念的理解也不同,需要根据上下文来解读。
1NF 一范式
实体的每个属性都是原子属性,不存在多值属性。无重复属性。
原子属性是指不可分割的属性,该属性所代表的概念要求必须将其内容视为一个整体。
多值属性是指在该系统内,一个属性可能有多种取值。比如需要存储电话与手机号,或要求存储多个手机号,却只提供一个名为“tel”的字段,在该属性里同时存储这多者,该属性就称为多值属性。
满足 1NF 才可以称之为关系型数据库。
不满足 1NF,也就是出现了多值属性,应该将多值属性拆成多个属性,比如上例需要分离出“固话”、“手机”两个字段。
我们假设其中手机号需要存储多个,那么有两种方案:
- 建立多个字段,如“手机1”、“手机2”。这显然不够灵活,也不方便使用。
- 建立第二张表存储手机号,这就需要再存储两张表的不同记录之间的关系,说的书面一点叫“分离出新的实体并与原实体建立一对多关系”,这需要定义主键,具体内容将在下文中详述。
函数依赖
让我们先学习一个术语。
设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,记作X→Y
。
这是说,像函数一样,给一个确定的输入(属性集X),有一个确定的输出(属性集Y)。
抽象的“关系模式”和具体存在的“关系”,下文统称“关系”。
如果X→Y
,但Y为X的子集, 则称X→Y
是平凡函数依赖。
如:关系R(Sno, Cno)
,依赖关系(Sno, Cno)→Sno
,(Sno, Cno)→Cno
都是平凡函数依赖。
如果X→Y
,但Y不为X的子集,则称X→Y
是非平凡的函数依赖。
如:关系R(Sno, Cno, Grade)
,依赖关系(Sno, Cno)→Grade
是非平凡函数依赖。
如果X→Y
,存在X的真子集X1,使得X1→Y
,则称Y部分依赖于X。也就是Y依赖于部分的X。
如:学生表(学号, 姓名, 性别, 班级, 年龄)
,(学号, 姓名)→性别
,学号→性别
,所以(学号, 姓名)→性别
是部分函数依赖。
如果X→Y
,但任何X的真子集X1都不存在X1→Y
则称Y完全依赖于X。
如:成绩表(学号, 课程号, 成绩)
,(学号, 课程号)→成绩
,学号!→成绩
,课程号!→成绩
,所以(学号, 课程号)→成绩
是完全函数依赖。
如果X→Y
,Y→Z
,X⊄Y
,Y!→X
,(X∪Y)∩Z=∅
,则称Z传递依赖于X。
如:关系S(学号, 系名, 系主任)
,学号→系名
,系名→系主任
,系名!→学号
,所以学号→系主任
为传递函数依赖。
函数依赖与属性的关系
设R(U)
是属性集U上的关系模式,X、Y是U的子集。
- 如果X和Y之间是一对一(1:1)关系,如学校和校长,则存在函数依赖
X→Y
和Y→X
。 - 如果X和Y之间是一对多(1:n)关系,如年龄和姓名,则存在函数依赖
Y→X
。 - 如果X和Y之间是多对多(m:n)关系,如学生和课程,则X和Y之间不存在函数依赖。
2NF 二范式
在不存在多值属性的基础上。要求定义主属性。实体的属性们要完全依赖于主属性。不存在非主属性对主属性的部分依赖。
主属性所属的字段称为主键。注意主属性是一个称为主属性的属性元组。当主键由多个字段组成时称之为复合主键。
元组是按特定顺序排列的一个或多个值。如 (1,2)
、(2,1)
、(1,2,3)
两两之间都是不同的元组。
定义了主键,每条记录就可以通过主属性来唯一标识、区分。这个主键本身不要求有意义,所以经常使用连续增长的数来作为主属性。
不满足 2NF,也就是出现了对主属性的部分依赖,那么需要将这部分属性根据其依赖的那部分主属性分离成新实体。
这是说,要将那些只依赖着“主键的一部分”的字段抽出来放入新表,以这“主键的一部分”作为新表的主键,删除原表里的这些字段。这在事实上形成了原表和新表的一对多关系。
3NF 三范式
在不存在多值属性、不存在实体的属性部分依赖于主属性的基础上。要求任何非主属性不依赖于其他非主属性。这称之为消除传递依赖。
这是说,要求 A 表中不包含那些已在 B 表中存在,却不是 B 表的主键的字段。也就是不能包含那些能够通过其他表主键推导出来的字段。
如果不满足该范式,则应精简使只有一个表保留该字段。
比如:以学号作为主键的学生信息表原本既存储了学生信息,也存储了学生的专业信息,要使其满足 3NF 则需从学生信息表里拆分出专业信息,组成以专业代号作为主键的专业信息表,然后学生信息里只存储其专业代号。学生信息表的专业代号字段只是一个外键,不是主键的一部分。
BCNF 巴斯-科德范式
在不存在多值属性、不存在实体的属性部分依赖于主属性、不存在非主属性依赖于其他非主属性的基础上。主属性内部不能有部分或传递依赖。这将消除对主属性子集的依赖,使主属性保持最简。
这是说,复合主键的元组要保持最简,不要存在主键的一部分能通过主键的另一部分经过某种方式得到的情况发生。
一般设计数据库,减少冗余到此范式即可。而为了性能考量减少查询次数等,冗余一些字段,以至于不能满足该范式,也是无可厚非的。
多值依赖
现在开始就是大量的理论了。考虑的也不是一条记录,而是多条记录之间发生的事。
多值依赖是属性之间的一对多关系,记为K→→A
。
函数依赖事实上是单值依赖,所以不能表达属性值之间的一对多关系。(有人称函数依赖为多值依赖的特例)
平凡的多值依赖:全集U=K+A
,一个K可以对应于多个A,即K→→A
。此时整个表就是一组一对多关系。
非平凡的多值依赖:全集U=K+A+B
,一个K可以对应于多个A,也可以对应于多个B,A与B互相独立,即K→→A
,K→→B
。整个表有多组一对多关系,且有:“一”部分是相同的属性集合,“多”部分是互相独立的属性集合。
例:关系模式WSC(W,S,C)
,W表示仓库,S表示保管员,C表示商品
假设每个仓库有若干个保管员,有若干个商品;每个保管员保管所在的仓库的多个商品;每个商品会被多个保管员保管。这张表满足W→→S
且W→→C
。这就是非平凡的多值依赖。
4NF 四范式
在不存在多值属性、不存在实体的属性部分依赖于主键、不存在非主属性依赖于其他非主属性、主属性内部不能有部分或传递依赖的基础上。
对每一个出现的非平凡的多值依赖K→→A
,K→→B
,分表。即消除多值依赖,只允许函数依赖。
连接依赖
TODO
5NF 投影连接范式
在不存在多值属性、不存在实体的属性部分依赖于主属性、不存在非主属性依赖于其他非主属性、主属性内部不能有部分或传递依赖、不存在多值依赖的基础上。
消除连接依赖,并且必须保证数据完整性。
关于设计范式的使用
关系模式的分解是为了适应实际应用,分解后减少了冗余,但查询时却要增加连接,效率下降,这都需要权衡。
因此,选取设计范式应立足实际应用,适当的冗余是可以容忍和必要的。一般应用到 BCNF 范式甚至最低到 2NF 都没问题。更高级别的范式,是拓展视野和深入学习的基础知识,了解它们也是必要的。
祝大家学习愉快。