基础认识
按照直观理解,数据库就是一个个表组成的,表里有很多记录和字段,各个表之间可能有相同的字段,以作为表彼此连结的纽带。但关系型数据库的体系里并没有表这个说法,更不是基于表来建立的,非常有悖于我的直观理解。经过临急抱佛脚和多年前糊里糊涂的专业课基础,我有以下想法:
关系型数据库的概念是基于全局的。可以想象成把不同表里的所有记录、字段都拆出来,变成一个个游离的数据块,也就是分量(Component)。这些数据块/分量有着不同的属性(Attribute),即原表中不同的列、字段;同时也属于不同的元组(Tuple),即原表中不同的记录。对于全局而言,不同的数据块有很多不同的属性,将这些数据块按照所属的属性分开,每一个属性下这些分量的集合,就是域(Domain),记为集合D1,D2......Dn;每个域里的出现的分量个数,就是基数(Cardinal number)。
到这里,基本概念差不多都梳理了。现在,对所有属性的域做笛卡尔积:D1x D2 x ...... x Dn = {(d1,d2,...dn) | di属于 Di, i=1,2...n},就得到了一个全局的大集合,可以说,数据库里所有的元组/记录都属于这个集合(但并不是集合里的元组都是实际表中的记录)。重点:这个大集合里的某些有限子集,称作关系(Relation),也就是直观认知里的表。
为什么叫关系呢?还是从全局来看,分量都是游离的,同时所属于某个元组和某个属性。将这些分量按照所属的元组分开,属于同一个元组的这些分量可以看作是在从不同的方面(属性)共同描述一类主体,在ER图中也叫做实体。也就是说,同一个元组下,这些分量是可以组合起来的,它们分别所属的属性也是可以组合起来的,不是风马牛不相及的。当分量按照一部分属性(字段)组合起来,共同描述一组元组(主体)的时候,这就是关系(表)。关系里属性的个数叫做目,或者度(Degree)。
码(Key)
超码(super key):在关系中,能唯一地标识一个元组的某一属性或属性组,可能含有无关属性。超码的任意子集还是超码;超码不唯一,最多的时候就是关系里所有属性的集合;
候选码(candidate key):在关系中,能唯一地标识一个元组的某一属性或属性组,不含有无关属性。候选码就是最小的超码,也是超码的一个子集。候选码也不唯一;
主码(primary key):从候选码中选定的一组属性组,是唯一的;
外码(foreign key):若关系 R1 的属性中包含关系 R2的主码 ,则这个属性(组)K就是R1的外码。R1 是 K 依赖的参照关系(referencing relation),R2 是 K 的被参照关系(referenced relation)
*码:能唯一确定一条记录的一个/多个属性。包括主码和候选码。任意一个候选码也能作为主键。其中主码/候选码的任意一个真子集都不能确定一条记录。
*主属性(Prime attribute):包含在任何一个候选码中的属性。
*非主属性或非码属性:不包含在任何码中的属性。
比较:超码>候选码>主码
函数依赖
设 R(U)是一个属性集 U 上的关系模式,X, Y, Z 是 U 的子集,X'是X的真子集。
函数依赖: 若对于 R(U)的任意一个可能的关系 r,r 中不可能存在两个元组在 X 上的属性值相等, 而在 Y 上的属性值不等, 则称 “X 函数确定 Y” 或 “Y函数依赖于X”,记作X→Y。即关系中属性或属性组X的值可以决定其它属性Y的值。
部分函数依赖:当X→Y时,若存在X'→Y,则称Y部分函数依赖于X。 例:姓名部分→(学号,身份证号),因为姓名同时也 部分→ 学号或者身份证号。
完全函数依赖:当X→Y时,若存在每一个X'都有X'!→Y,则称Y 完全→X。 例:姓名 完全→(班级,座位号),因为姓名!→班级或者座位号。
传递函数依赖:当X→Y,Y!→X,Y→Z时,则称Z传递函数依赖于X,即X→Z。 例:姓名→成绩,但成绩!→姓名,成绩→排名,所以姓名→排名。
范式
第一范式(1NF):关系R的所有属性都是不可分割的基本数据项。是对关系模式的基本要求,不满足第一范式(1NF)的数据库就不是关系数据库;
第二范式(2NF):符合1NF,每一个非主属性都完全函数依赖于码;
第三范式(3NF):符合2NF,每一个非主属性都不传递依赖于码;
第四范式(BCNF):符合3NF,所有非主属性对每一个码都是完全函数依赖;所有的主属性对每一个不包含它的码,也是完全函数依赖;没有任何属性完全函数依赖于非码属性。即消除任何属性对码的部分和传递函数依赖。
下面以一个实例来解释4个范式的区别和联系:
第一范式(1NF)实例
首先,存在关系R1(学号,院系,宿舍,课程,成绩),假设同一个学院住在同一栋宿舍,码为(学号,课程)。其中函数依赖有:
(学号)完全→(院系),(院系)完全→(宿舍),所以(学号)传递→(宿舍);
(学号,课程)完全→(成绩),所以(学号,课程)部分→(院系)或(宿舍)。
由此可知,R1属于1NF,但不属于2NF。在实际操作中,这样的R1存在很多问题:未选课的学生无法进入表(插入异常);只选修一门课的学生一旦退课,所有其他信息都被删除了(删除异常);如果学生要转系,需要更改他的所有选课记录(修改复杂和数据冗余度大)。
因此,这并不是一个好的关系模式,关键是因为存在对码的冗余依赖:院系和宿舍部分依赖于码(学号,课程),对于课程的任何操作都会影响院系和宿舍。若将R1分解成两个关系模式:R2a(学号,课程,成绩),R2b(学号,院系,宿舍),让每一行的数据只能与其中一列相关,即一行数据只做一件事,就可以消除这些部分函数依赖了。
第二范式(2NF)实例
现在,存在R2a(学号,课程,成绩),码为(学号,课程);R2b(学号,院系,宿舍),码为(学号)。此时函数依赖有:
R2a:(学号,课程)完全→(成绩)
R2b:(学号)完全→(院系),(院系)完全→(宿舍),所以(学号)传递→(宿舍)
由此可知,消除了部分函数依赖以后,R2a和R2b都属于2NF。但在R2b中,如果学校重新安排院系对应的宿舍,将需要修改所有学生的院系记录,依然存在着修改复杂和数据冗余度大的问题。这是因为依然存在对码的冗余依赖:宿舍传递依赖于学号,任何关于院系自身的操作都会影响学生的院系记录。因此,若将R2b分解成R3a(学号,院系),R3b(院系,宿舍),就可以消除这些传递函数依赖了。
第三范式(3NF)实例
现在,存在R3a(学号,院系),R3b(院系,宿舍)。为了说明问题,我在R3a中增加几个属性,变为R3a(学号,姓名,院系号,院系名),考虑到可以会同名,但院系号和院系是一一对应的,码可以有两个:(学号,院系号),(学号,院系名)。此时函数依赖有:
R3a:(学号)完全→(院系),(姓名)完全→(院系)
由此可知,消除了传递函数依赖以后,R3a和R3b都属于2NF。
参考自:
数据库复习基本概念——关系、元组、属性、码、域、分量、形式化定义 - 作业没带的小明
通过示例理解数据库相关概念(一、关系,元组,域,键,笛卡儿积等等)- flying_monkey_1
网友评论