跳至主要內容

关系数据库设计理论

njrdatabasebackend数据库设计理论大约 6 分钟约 1832 字

关系数据库有一个成熟的理论——依赖。它涉及到如何构建一个良好的关系型数据库模式,以及当一个模式存在缺陷时应该如何改进。

函数依赖

定义

函数依赖:设 R 是一个关系模式,XYR 的属性集,如果对于 R 的任意两个元组 t1t2,如果 t1t2X 上的分量相等,则 t1t2Y 上的分量也相等,那么我们说 Y 函数依赖于 X,记作 X -> Y

以关系 Movies1 为例。

titleyearlengthgenrestudioNamestarName
The Godfather1972175CrimeParamountMarlon Brando
The Godfather1972175CrimeParamountAl Pacino
The Godfather1972175CrimeParamountJames Caan

Movies1 包含了更多的属性,这种模式设计并不是很好。

该关系有如下 FD:

title year --> length genre studioName

这个 FD 的含义是:如果两个元组在 titleyear 上的分量相等,则它们在 lengthgenrestudioName 上的分量也相等。

因此,希望给定 titleyear 就能确定一部电影,进而确定 lengthgenrestudioName

另外,可以看到下面的 FD,是错误的,它不是一个函数依赖。

title year --> starName

给定一个 titleyear,可能有多个 starName,因此 starName 不是函数依赖于 titleyear

关系的键

:如下面条件满足,则认为一个或多个属性集 {A1, A2, ..., An} 是关系 R 的键。

  • 这些属性函数决定关系的所有其他属性;
  • {A1, A2, ..., An} 的真子集中,没有一个函数能决定 R 的所有其他属性。

当键只包括一个单独的属性 A 时,称 A 而不是 {A} 是键。

提示

一般使用 id 确定唯一主键。

关系数据库模式设计

不仔细选择关系数据库模式会带来冗余和相应的异常,关系 Movies1 为例。

电影 The Godfather 的长度和流派对参演的每个影星都重复了一次,这些信息的重复是冗余的。

异常

当试图在一个关系中包含过多的信息时,产生的问题称为冗余,冗余数据会导致以下三种异常:

  • 冗余,信息没有必要在多个元组中重复。
  • 更新异常,如果信息在多个元组中重复,那么更新数据库时就必须在多个地方进行更新,否则会导致不一致。
  • 删除异常,如果从影星集删除 Al Pacino,则数据库中将不在包含这部电影的影星,关系 Movies1 中关于 Al Pacino 的元组就会消失,而且他包含的其他信息如片长 175 分钟等也会在数据库中消失。

分解关系

一般用分解关系的办法消除异常。关系 R 的分解涉及分离 R 的属性,以构造两个新的关系模式。

将关系 Movies1 分解成 关系 Movies2 和关系 Movies3

  • Movies2 包含了除 starName 外的其他所有属性。
  • Movies3 包含了属性 titleyearsstarName

Movies2

titleyearlengthgenrestudioName
The Godfather1972175CrimeParamount

Movies3

titleyearstarName
The Godfather1972Marlon Brando
The Godfather1972Al Pacino
The Godfather1972James Caan

上述所说三种异常都被解决了:

  • 冗余被消除了,关系 Movies2 中电影的片长只出现了一次。
  • 更新异常的风险被消除了,因为关系 Movies2 中的每个电影只出现一次,所以只需要在一个地方更新。
  • 删除异常的风险也被消除了,因为关系 Movies3 中的每个影星只出现一次,所以只需要在一个地方删除。

设计范式

第一范式

第一范式(first normal form, 1NF)只简单地要求每个元组的每个属性都是不可分的(属性具有原子性)。

第一范式需要根据系统的实际需求决定。比如某些数据库系统中需要用到「地址」这个属性,本来直接将「地址」属性设计成一个数据库表的字段就行。

但如果系统经常会访问「地址」属性中的「城市」部分,那么就需要将「地址」这个属性重新拆分为省份、城市、详细地址等多个部分进行存储,这样在对地址中某一部分操作的时候将非常方便。

这样设计才算满足了数据库的第一范式。

编号姓名性别年龄联系电话省份城市详细地址
1张三201234567湖北武汉123 号
2李四181234568湖北武汉456 号
3王五221234569湖北武汉789 号

第二范式

第二范式(second normal form, 2NF)要求关系模式必须是 1NF,并且每个非主属性完全函数依赖于任何一个候选键。也就是说在一个数据库表中,一个表中只能保存一种数据,不可以把多种数据保存在同一张数据库表中。

提示

候选键(candidate key):由关系的一个或多个属性组成,候选键都具备键的特征,都有资格成为主键。

比如要设计一个订单信息表,因为订单中可能会有多种商品,所以要将订单编号和商品编号作为数据库表的联合主键。

订单编号商品编号商品名称商品价格商品数量单位客户所属单位联系方式
0011苹果510张三单位 11234567
0022香蕉320张三单位 11234567
0033苹果510李四单位 21234568

这样就产生一个问题:这个表中是以订单编号和商品编号作为联合主键。这样在该表中商品名称、单位、商品价格等信息不与该表的主键相关,而仅仅是与商品编号相关。所以在这里违反了第二范式的设计原则。

而如果把这个订单信息表进行拆分,把商品信息分离到另一个表中,把订单项目表也分离到另一个表中,就非常完美了。如下所示。

订单信息表

订单编号客户所属单位联系方式
001张三单位 11234567
002张三单位 11234567
003李四单位 21234568

商品信息表

商品编号商品名称商品价格单位
1苹果5
2香蕉3
3苹果5

订单项目表

订单编号商品编号商品数量
001110
002220
003310

第三范式

第三范式需要确保数据表中的每一列数据都和主键直接相关,而不能间接相关。

上述表中存在一个问题:订单信息表中的客户和联系方式与订单编号直接相关,而与主键无关。所以这里违反了第三范式的设计原则。

所以需要将订单信息表进行拆分,把客户信息分离到另一个表中,把订单信息表也分离到另一个表中,就非常完美了。如下所示。

客户信息表

客户编号客户联系方式
1张三1234567
2李四1234568