目录 / Table of Contents
本文是《数据库系统概论》的第6章以及“Database System: The Complete Book”的第三章所作的部分笔记,用以理解数据库设计过程中的关系数据理论。笔者认为,在这些教材中,对于关系数据理论的阐释可被归结为对于以下几个问题的回应:
- 一个糟糕的数据库设计是什么样的,又会为使用者带来多少麻烦?(问题的引入)
- 是否存在某些可以遵循的准则,帮助我们设计出“不那么糟糕”的数据库?(规范化理论)
- 在了解“不那么糟糕的数据库应该是什么样的”之后,我们又能采取哪些措施,让我们在设计数据库时满足这些让数据库运作得更好的条件?(Armstrong公理&模式分解)
预备知识
为了更好地理解下文的内容,读者首先需要掌握关系数据理论中的部分基本概念:
函数依赖(Functional Dependency)
- 函数依赖:设 R(U) 是属性集U的关系模型, X, Y是U的一个子集, 对于 R(U) 中的任一个关系 r, 不可能存在两个元组在 X 上属性值相同, 而在 Y 上属性值不同。则称 X 函数确定 Y , 或 Y 函数依赖 X ,记作$X \rightarrow Y$。
- 完全函数依赖:如果Y函数依赖X,但不依赖X的任何一个真子集,则称Y完全函数依赖X,记作$X \xrightarrow F Y$。
- 部分函数依赖:如果Y函数依赖X,但依赖于X的任何一个真子集,则称Y部分函数依赖X,记作$X \xrightarrow P Y$。
- 传递函数依赖:设Z也是U的一个子集。如果X决定Y,Y决定Z,且Y不决定X,那么称Z对X传递函数依赖,记作$X \xrightarrow {传递} Y$。
码/键(Key)
-
候选码:设属性集U包含关系模式内所有可能的属性值,K是U的子集,若$K\xrightarrow F U$,则称K为候选码(K是一个集合!)
-
主码:候选码若有多个,则选定其中的一个,称为主码。
-
超码:对于属性集U,候选码K,若$K\xrightarrow P U$,则称K为超码。
-
主属性/非主属性:包含在任何一个候选码的属性都叫主属性,其他都叫非主属性。
范式(Normal Form)
对于范式的概念,特别不靠谱的理解如下:
-
第一范式(1NF):不能出现类似excel中合并单元格的那种情形?(要求一个关系中的所有字段值都是不可分解的原子值)
-
第二范式(2NF):在满足1NF的基础上,还需要满足:在其他非主属性与主键的函数依赖关系中,主键集合中是作为一个整体起到函数决定的作用的。反过来说,就是不能出现这样一种情况:主键中的某个主属性仅凭自身便能决定其它的非主属性,而函数决定其它的非主属性主键只有作为包含了(不存在非主属性对主键的部分函数依赖关系)。
-
第三范式(3NF):在满足2NF的基础上,对于一个关系内的非主属性,他们能且仅能被主键唯一地表示。换而言之,该关系模式内存在的函数依赖关系都是由主键所决定的。除了这些被主键决定的函数依赖关系之外,非主属性之间必须相互独立,再也不能出现其他的函数依赖关系。
-
BCNF: 在满足3NF的基础上,将“对于一个关系内的非主属性,他们能且仅能被主键唯一地表示”成了“对于一个关系内的所有属性,他们能且仅能被主键唯一地表示”,所需要满足的条件比3NF更加严格。
-
第四范式(4NF):原关系模式中不存在非平凡的多值依赖。
问题的引入——数据异常及规范化理论
当我们在设计数据库时,若我们考虑不当,将过多的要素全部塞进了一张单独的表时,那么后续操作这张表时可能会引发诸多意料之外的异常情况(anomality),如:
- 数据冗余 (Redundancy) :在某些元组中,部分信息会重复出现,占用了不必要的存储空间。
- 更新异常 (Update Anomalities) :在更新该表中的信息时,可能导致数据库中的数据不一致。
- 删除异常 (Deletion Anomalities) :在删除某个元组的信息时,可能删除其它不应该删除的信息。
由于笔者太懒异常操作的例子实在过于常见的缘故,笔者在此便不列举具体的例子了。我们更关心的是,这些异常背后到底意味着关系模式设计中的哪些不良性质?
在第2小节里,我们将了解如何通过函数依赖描述一个关系模式。将借助函数依赖刻画关系模式的规范化程度,根据该关系模式属性之间的依赖程度,将该关系模式区分为某一种特定范式,以甄别该关系模式中是否存在引发上述问题的糟糕性质。接着,在第3小节内,我们将了解如何根据特定的规范化程度,对于关系模式进行规范化,将具有糟糕性质的关系模式转化为更合适的形式。 一个设计不当的数据库在操作时之所以会出现那么多异常,其根源在于我们在设计关系模式时忽视了不恰当的部分/传递函数依赖(无论是有意的,还是无意的)。如果我们能事先识别出关系模式中出现的所有可能的函数依赖,那不就可以直接把那些不恰当的函数依赖挑出来,把它们扼杀在摇篮里了吗?规范化的基本思想可被归结为**“一事一地”**,即让每一个关系只描述一个概念、一个实体或者实体之间的联系。这种规范化,可以通过模式分解的手段完成,将低一级的关系模式分解为多个高一级的关系模式,从而消除关系模式中不合适的数据依赖关系。
对这个问题换一个更为具体的表述,那便成了:
给定关系模式R <U, F>以及函数依赖集F的前提下,是否存在一种方法,which能帮助我们找出该关系模式中被F逻辑蕴含的函数依赖的全体。
首先,通过定义逻辑蕴含的概念,以准确的刻画出问题的定义。然而,在描述完上面这个问题以后,我们还是一筹莫展——光从这个定义本身而言,我们得不到什么有用的信息,有助于求解该问题。于是,机智的老哥们曲线救国,试着以全新的角度解读这个问题,重新构造了一个推理系统,即Armstrong公理,在逻辑上证明了“逻辑蕴含”与“根据Armstrong公理进行推导”这两个操作之间是等价的(有效性与完备性的证明)。