本节书摘来华章计算机《SQL与关系数据库理论——如何编写健壮的SQL代码》一书中的第1章 ,第1.6节 C. J. Date 著 单世民 何英昊 许侃 译 更多章节内容可以访问云栖社区“华章计算机”公众号查看。
1.6 关系的性质
现在再重新检视一下基本的关系概念。在本节中,我要关注“关系”的一些具体性质。首先,每个关系都有一个标题(heading)和一个主体(body):标题是属性的集合(对于“属性”,我指的是“属性名/类型名配对”,并且同一标题中的属性不得重名),主体是符合标题的元组的集合。比如,图1.3*应商关系的标题有4个属性,其主体有5个元组。因此,关系实际上包含的不是多个元组,它包含的是一个主体,而主体中包含多个元组。为了简化,我们在谈论时就好像关系直接包含多个元组。
虽然“标题由属性名/类型名配对组成”这样的说法是最正确的,但通常还是如图1.3中那样省略类型名称,就好似标题只包含一系列属性名。比如,STATUS属性确实是有类型的(即INTEGER),但是在图1.3中并不显示。然而,你要知道是有类型存在的。
标题中的属性个数就是度(degree),也叫“元”(arity),主体中元组的数目就是基数(cardinality)。比如,图1.3中关系S的度为4,基数为5;类似的,关系P度为5,基数为6;关系SP的度为3,基数为12。注意:度也用于元组。注9比如,关系S所有元组(和关系S本身一样)度都为4。
关系从不包含重复元组。这一性质是成立的,因为主体定义为元组的集合,而数学中的集合不包含重复元素。然而, SQL不是这样:一般情况下,SQL表允许包含重复行,所以SQL表不是关系。因此,本书中总是使用术语“关系”即指依据定义没有重复元组的关系,而不是指SQL表。依据定义,关系运算总是产生没有重复元组的结果。比如,图1.3*应商关系的CITY属性的投影会产生如下图左半部分显示的结果,而不会产生下图右半部分的结果。
左半部分的结果可以通过SQL查询SELECT DISTINCT CITY FROM S得到。忽略DISTINCT会导致右侧非关系化的结果。尤其要注意右侧的表没有双下划线,那是因为它没有键,当然也更不会有主键。
关系的元组从顶到底是无序的。此性质成立也是因为主体是定义为集合的,而数学中集合的元素是无序的(比如,{a,b,c}和{c,a,b}在数学中是同一个集合,同样的说法自然也适用于关系模型)。当然,在纸面上图示关系的时候,我们必须以某种自顶至底的顺序来显示行。然而,这个排序并不对应任何关系化事物。以图1.3中所示的供货商关系为例,其中的行可以用自顶至底的任何顺序显示(比如,先是S3,然后是S1,接着是S5,再是S4,最后是S2),而得到的图表示的仍然是同一个关系。注意:关系的元组没有排序并不意味着查询不能包括ORDER BY声明,但是这一点却说明了这样的查询生成的结果不是关系。ORDER BY对于显示结果很有用,但并不是关系运算符。
同样,关系的属性自左到右也是无序的。这是因为标题也是数学的集合。在纸上图示一个关系的时候,我们也必须以某种顺序显示列,但是此排序也不对应任何关系化事物。以图1.3中所示供应商关系为例,其中的列可以用自左至右的任何顺序显示(比如,按照STATUS,SNAME,CITY,SNO的顺序),而得到的图表示的仍然是原先那个关系模型中的同一个关系。需要注意的是,SQL在此处仍然是不一样的:SQL的表对列具有自左至右的排序(这也是SQL表为什么不是关系的另一个原因)。比如,下面两张图表示的就是同一个关系,但却是不同的SQL表:
对应的SQL查询分别是SELECT SNO、CITY FROM S和SELECT CITY、SNO FROM S。你可能认为这两个查询以及这两个表之间的区别并不十分重要;然而,这些区别有一些重要结果,其中一些会在后续章节中涉及(比如,第6章对SQL显式JOIN运算符的讨论)。
最后,关系始终是规范化的(normalized)(或者说,它们属于第一范式,1NF)。注10通俗地讲,该含义就是:若用表格化方式图示关系,则每个行和列的交叉处总是仅有一个单一值。更规范的说法是:它意味着每个关系中的每个元组在每个属性的位置都只包含某个类型的单一值。注意:我会在下一章针对此问题进行大量说明。
在本节结尾,我还是要强调我已经说过好多遍的事:即,“关系”和“关系的图示”(就像图1.1和图1.3那样的)是存在逻辑差异的。图1.1和图1.3中的结构根本不是关系而是关系的图示(通常称其为“表”,尽管“表”在SQL上下文中有很多含义)。当然,关系和表确实有一些相似之处,并在非正式场合常把它俩说成是一个东西,这通常也是可以接受的。但是在我们需要精确(我马上就要尝试精确些)的时候,我们就必须认识到这两种概念是不同的。
顺便说一句,任何类型的东西和它的图示之间都是有逻辑差异的。Magritte的一个名画就很好地诠释了这一观点。这幅名画画的是一个普通的烟斗,而Magritte却在下面写着“Cei n抏st pas une pipe”。此处的要点在于画显然不是烟斗,而只是烟斗的图示。
尽管如此,我还是要说:关系模型的基本抽象对象(即,关系)有着如此简单的表示方式,而此种简单的表示方式使得关系系统很容易使用和理解,也使得推理判断系统行为变得很容易,这实际上是关系模型的一个主要优点。然而,也正是这种简单的表示方式暗示着一些事情并不成立(比如,自顶至底的元组排序)。
更为深入的一点是,关系和关系的图示是存在逻辑差异的。逻辑差异的概念源于Wittgenstein的格言:
所有的逻辑差异都是大差异。
这是个特别有用的观点。作为“思想工具”,此观点对于明晰和精确思考很有裨益,而且它对于澄清和分析一些在数据库世界中已经太常见的混淆也非常有帮助。我会在后续内容中多次采用此观点。同时应该指出,我们已然见过了一些重要的逻辑差异,其中一些如下:
- SQL vs. 关系模型;
- 模型 vs. 实现;
- 第一种释义下的“数据模型” vs. 第二种释义下的“数据模型”。
在后续内容中我们会遇到更多。
一些关键点
后续章节(尤其是第3章)中将要详细讨论的关键点如下:
- 元组的每个子集仍然是元组:比如,考虑图1.3*应商S1的元组。此元组有4个分量,分别对应4个属性,SNO、SNAME、STATUS和CITY。如果我们去除了SNAME分量,那么剩余部分实际上仍然是一个元组;即具有3个分量的元组(度为3的元组)。
- 标题的每个子集仍是标题:比如,考虑图1.3*应商关系的标题。此标题有4个属性,SNO、SNAME、STATUS和CITY。如果我们去除了SNAME和STATUS属性,那么剩下的仍然是一个标题(度为2的标题)。
- 主体的每个子集仍是主体:比如,考虑图1.3*应商关系的主体。此主体有5个元组,分别对应5个供应商,S1、S2、S3、S4和S5。如果我们去除S1和S3元组,那么剩下的仍然是一个主体(基数为3的主体)。
注意:本书依照常规使用“B是A的子集”形式的表达式来包括A和B相等的情况。因此,每个元组就是其自身的子集(对每个标题、每个主体也是如此)。如果想排除这种可能性,可以直接采用真子集(proper subsets)方式表达。比如,常用的供应商S1的元组当然是其自身的子集,但不是自身的真子集。进一步讲,前面的说明稍加改变也同样适用于超集。比如,供应商S1的元组是其自身的超集,但不是自身的真超集。注11
相等(equality)是一个重要的概念,特别是它还用于元组和关系。一般而言,当且仅当两个值是同一个取值的时候两个值才是相等的。比如,整数3等于整数3而不等于其他东西(尤其不等于其他整数)。同样,当且仅当两个元组是同一个元组的时候两个元组才是相等的。以图1.1为例,供应商S1的元组等于供应商S1的元组而不等于其他东西(尤其不等于其他元组)。换句话说,当且仅当:(a)它们包含完全一样的属性;(b)对应属性的取值依次相等时两个元组是相等的。
而且,当且仅当两个元组相等的时候,两个元组是互为重复的(此点虽然看似明显,但还是要提到的)。
关系也是完全一样的。当且仅当两个关系是同一个关系时两个关系才是相等的。以图1.1为例,供应商关系等于供应商关系而不等于其他东西(尤其不等于其他关系)。换句话说,当且仅当两个关系的标题是相等的,且它们的主体是相等的时候,两个关系才是相等的。
第3章将再讨论这些问题。现在我只想再多说一句:我们会看到,“元组相等”是特别基础性的概念,几乎关系模型的所有内容都决定性地依赖于它。