本节书摘来自异步社区出版社《视图更新与关系数据库理论》一书中的第2章,第2.6节,作者:【美】C.J. Date(达特),更多章节内容可以访问云栖社区“异步社区”公众号查看。
2.6 数据库与数据库变量
这是本章的最后一节。在本节中,我希望你们能注意一个重要的事实,正如关系值与关系变量(relvars)之间有逻辑区别一样,数据库值和数据库变量(dbvars)之间也存在着逻辑差异。下面一段引自《宣言》那本书。
第 1 版的“宣言”将数据库的本质(也就是数据库的值)与数据库变量进行了区分……[同时也]指出未明确定义的词汇“数据库”应该就是明确地代表数据库的值,并介绍了作为“数据库变量”的缩写来使用的词汇“dbvar”。虽然我们依然相信这个区别本身是真实有效的,但随即我们发现其实它与“宣言”所要讲的其他方面内容几乎没什么关系。因此我们决定,为了文章的易读性,还是在文中使用传统的说法。19[19]
于是,当我们“更新某些关系变量”(在某个数据库内部)时,实际上我们更新的是相应的数据库变量。(为了表述清晰,在本节的其余部分我都将使用数据库变量这个词。)例如,Tutorial D声明
DELETE ( SP WHERE QTY < 150 ) FROM SP ;
“更新设备供应关系变量SP”其实是更新了整个供应商-零部件数据库变量—对这个数据库变量的“新的”数据库值和“老的”值基本一样,除了特定的“设备供应”数组被移除了。换句话说,有时候我们会说一个数据库“包含变量”,即相应的关系变量,其实这种说法很笼统,而且很不正式。更加正式的并且更准确的描述方式应该是这样的:
数据库变量就是数组变量。
数组变量对该数据库变量中的每一个关系变量都拥有一个唯一的属性(并且没有其他属性),并且每个属性都有关系值。我们举例来说,在供应商-零部件数据库中,我们可以将整个数据库变量想象成一个数组变量(也可以用“tuplevar”表示),数组类型如下:
TUPLE { S RELATION { SNO CHAR , SNAME CHAR ,
STATUS INTEGER, CITY CHAR } ,
P RELATION { PNO CHAR , PNAME CHAR ,
COLOR CHAR , WEIGHT RATIONAL , CITY CHAR } ,
SP RELATION { SNO CHAR , PNO CHAR , QTY INTEGER } }
让我们暂时称供应商-零部件数据库变量(或者也可以叫它数组变量)为SPDB。20[20]那么上面我们刚刚看到过的DELETE声明可以作为下面的“数据库”(以及数组)赋值。
SPDB := TUPLE { S ( S FROM SPDB ) ,
P ( P FROM SPDB ) ,
SP ( ( SP FROM SPDB ) WHERE NOT ( QTY < 150 ) ) } ;
解释说明:这个赋值右侧的表达式表示一个数组拥有3个属性分别叫作S、P以及SP,每一个属性都拥有关系值。21[21]在数组内,属性S的值就是当前关系变量S的值,属性P的值就是当前关系变量P的值,而属性SP的值则是在当前关系变量SP的值的基础上,减去那些数量小于150的数组。
我们来看另一个例子,这里我们再次使用“双删除(DELETE)”,我在本章早些时候介绍多重赋值概念的时候曾经使用过。
DELETE ( S WHERE SNO = 'S1' ) FROM S ,
DELETE ( SP WHERE SNO = 'S1' ) FROM SP ;
现在我们观察这个关系赋值,很明显是个多重赋值,会发现它逻辑上等价于下面的“单一”数据库(以及数组)赋值。
SPDB := TUPLE { S ( ( S FROM SPDB ) WHERE NOT ( SNO = 'S1' ) ,
P ( P FROM SPDB ) ,
SP ( ( SP FROM SPDB ) WHERE NOT ( SNO = 'S1' ) ) } ;
1liI
换句话说,目标变量全部都是同一个数据库(或者说同一个数据库变量)中的关系变量,多重赋值其实仅仅就是一个语法工具,它可以让我们将逻辑上的“数据库”赋值表达成一个一系列独立“关系”赋值的组合(请参看下面关于此点更多的讨论)。而一个“单一”的关系赋值,例如由只针对一个单独的数据库变量进行的一个单独的赋值所组成的“多重”赋值—仅仅是个特例。从根本上来说,事实上目前对数据库更新来讲,数据库变量是唯一真正的变量。这就是说,数据库更新其实“总是”对某些数据库变量进行更新,而对单一一个数据库关系变量的更新仅仅是特例而已。
根据上面所有的内容,如果目标关系变量是一个数据库关系变量(就是说,如果这个目标关系变量是某个数据库中的关系变量),那么即使一个“单一”关系赋值其实也是一个数据库赋值。当然,这个数据库赋值像所有的赋值一样,都必须遵循“赋值原则”,并且正由于它是一个数据库赋值,它也同样需要遵循“黄金法则”。
我们前面讲到的这些想法太重要了,它值得我们再次复习一遍,只是说法有轻微的不同。首先,数据库变量是数组变量,数据库(也就是在给定的时间,给定的数据库变量所具有的值)是所有属性都有关系值的数组。第二,给定一个关系赋值如下面格式。
R := rx
(这里R是一个关系变量参考量,从语法上讲就是一个关系变量的名称,指出了一些数据库关系变量,而rx是一个关系表达式,表示关系r与R具有相同类型)这个关系变量参考量R其实是一个“伪变量”参考量(请立刻参看本段下面的注解)。换句话说,关系赋值其实是针对相对应数据库变量(再次重申它就是数组变量)的一个部分进行“替换”的简称化表达方式。因此我们推断出“关系变量”(至少是在数据库中的关系变量)并不是真正的变量,它们只是一些让人觉得很便利的虚构的变量,能够制造出这种假象:数据库或数据库变量可以一点一点地、一个关系变量一个关系变量地被更新。我们进一步可以推断出,在某种意义上,“关系赋值”(不管是不是多重的)也同样是个方便的假象,具体来说就是它是一个虚拟操作,让我们感觉好像可以通过一系列针对这个数据库内的单独的关系变量的更新,来实现对数据库变量的更新。22[22]
关于伪变量的注解:从本质上讲,伪变量是这样一个概念,或者是这个概念的象征性的名字,它看起来像是个可操作的表达式,但不同的是它并不表示任何值;相反地,它出现在某些赋值中的目标位置,来表示“变量的一部分”。我们举个例子,令X为一个CHAR类型的变量,并令X目前的值是“antimony”。那么赋值表达式SUBSTR(X,5,3) := ‘nom’的效果就是“替换”X的第5~7个字符,将“mon”替换成“nom”,并因此将X的“旧的”值(“antimony”)完全替换成“新的”值(“antinomy”)。这个赋值左侧的SUBSTR(X,5,3)就是一个伪变量参考量,而这个赋值本身则是下面表达式的简化版:X := SUBSTRX(X,1,4) || ‘nom’ || SUBSTR(X,8,1),在这里符号“||”表示字符串连接。注解完毕。
为了本书的易读性和定义的明确性,虽然有违我的最佳判断,但我还是决定在本书余下的部分不再使用数据库变量和“dbvars”这两个专用术语,至少不会用很多次,而是使用大家熟悉的“数据库”,希望我要表达的意思在上下文中总是很清楚。读者要擦亮眼睛。
[1] 说这个语言是假设的,仅仅是因为到我撰写本书的时间为止,市场上还没有一个正式发售的商业版本。但是它的设计原型已经存在,可以通过下面的网址来访问:www.thethirdmanifesto.com (请参看下面紧跟着的另一个注脚)。
[2] Tutorial D从该书第1次出版以来已经被修订和扩展。关于新版本(已经有改善,但和本书中使用的并不完全相同)的描述可以在我和Hugh Darwen合著的《Database Explorations: Essays on The Third Manifesto and Related Topics》(Trafford, 2010)中找到,同样可以访问网站www.thethirdmanifesto.com来获得相关资讯(这本书同时还有很多其他关于“宣言”的内容)。
[3] “逻辑差异”这个概念,是我和Darwen在撰写技术文章时很爱使用的一个概念(特别是在关于《The Third Manifesto》的文章中),它来源于Wittgenstein(英国哲学家、数理逻辑学家维特根斯坦)的一句名言:All logical differences are big differences(所有逻辑上的差异都是巨大差异)。
[4] “缩写”在这里用引号括起来是因为其实它要比需要缩写的部分还长。但是一部分原因其实应该归咎于我使用的是不规范的Tutorial D术语(在真正的Tutorial D语法中,DELETE语句应该写成DELETE S WHERE CITY = ‘London’)。在本书中我特意选择了稍微啰嗦一点的语法来表达DELETE(和INSERT),这也是为了让它们的语义足够清晰。
[5] 严格来说,d和i当然不仅仅是“数组的集合”,而是和r同一类型的关系。
[6] 请参看附录A中对此命题的证明。
[7] 真正在Tutorial D中对于多重赋值的语句应当是DELETE R d, INSERT R i。
[8] 如果遇到两个或更多独立赋值拥有相同的目标变量,那么这个定义就需要被改进,不过目前我们还用不到这些。可以参看附件A来获得更多解释说明。
[9] 我将在这里和全书其他地方都使用这种简单的表示法来表示数组,以便大家阅读。
[10] 这个赋值在等号右侧的表达式是一个“关系选择器调用”,其实就是一个关系“字面量”。如果你想获得对相关内容的详细解释,请参考《SQL and Relational Theory》。
[11] 关系变量S是个基础变量,这是肯定的。关于视图的情况,请见第3章。
[12] 这里也有例外,有些约束可以用KEY和FOREIGN KEY的方式来表达,但即使是这些约束本质上也仅仅是约束的缩写形式,它们都可以使用CONSTRAINT清晰地进行声明,只不过会比较冗长。请参看《SQL and Relational Theory》一书来获得关于这一点更深入的讨论信息。
[13] 我在这里简单一点说:约束的效果很强大,一些关系选择器调用确实会让所需类型的关系检查得“更迅速”,甚至把检查作为得出关系选择器调用结果的流程中的一环。同样,请参看《SQL and Relational Theory》一书来获得关于这一点更深入的讨论信息。
[14] 这段注文是从《Database Design and Relational Theory》中截取过来的,我进行了少量编辑。注意:我曾经在前言就提醒过你,在本书中我会使用《Database Design and Relational Theory》作为缩写的形式来表示引用我的《Database Design and Relational Theory: Normal Forms and All That Jazz》(奥莱利,2012)。
[15] 我想强调“当且仅当”。这里至少有一个重要的推论。具体来说就是令关系变量R1和R2分别拥 有谓词P1和P2,那么当P1和P2同时被同一个数组t满足时,t必须同时出现在R1和R2中。 (虽然这是一种经验法则,但它可能是一个很好的方式来确保P1和P2足够特殊以排除前面的可能 性。但是请注意,在第4章以及第9~11章我们将看到一些蓄意违反这个原则的情况出现。)
[16] 这里我要说明,我们发现一件很便利的事情,就是在Tutorial D的所有操作中,可以把最高优先级 设给投影。
[17] 因此,我们可以将闭域假设加以扩展。在一个给定的时间内,我们设一个命题p在对应数组t并且 在数组t满足下列条件时为真:(a)数组t在该时间(前面所说的给定的时间)内出现在某个数据 库的关系变量中,或者(b)数组t在该时间内出现在某个关系中,并且可以由该关系推导出数组 t就是这个数据库关系变量的值。我们换一句简单点的话来说,就是所有由数据库声明或说明的均 为真,其他均为假。
[18] 我做这个标记是为了说明著名的关系差操作—Tutorial D中的MINUS是NOT MATCHING的 一个特例。具体来说就是如果关系r1和r2是相同类型,那么r1 NOT MATCHING r2会简化为r1 MINUS r2,这一点可以很容易确认。因此,相对来说NOT MATCHIG比MINUS更加基础。
[19] 换句话说,我们继续使用“数据库”这个词,时而表示数据库变量,时而表示数据库值,并且我们 压根不使用database variable或者dbvar这两个词。但是后来我们发现,因为种种原因我们没有设 计这部分是一个非常错误的决定。(做一些你知道的逻辑上错误的事情通常不是个好主意。)至于我 在本书中将计划如何来使用这个术语,请参看本节剩余的部分。
[20] 如果我们想要像这个例子中一样写出清晰的数据库赋值,那么数据库变量就必须拥有用户可见的名 字,但在Tutorial D中它们没有(至少按照目前的定义来说)。
[21] 实际上,上面这个表达式是一个数组选择器调用。再次说明,如果你想获得关于这个的更多说明, 请参看《SQL and Relational Theory》。
[22] 从这个讨论中我们可以看出多重关系赋值是极其重要的(在缺少明确的对数据库赋值的支持的情 况下),实际上它对于后面章节要讨论的一些特定的视图更新规则来说非常的关键。因此在这种情 况下,我要说,请原谅我在这里指出,很不幸,至少SQL不支持这个操作。
本文仅用于学习和交流目的,不代表异步社区观点。非商业转载请注明作译者、出处,并保留本文的原始链接。