本节书摘来自异步社区出版社《你不可不知的关系数据库理论》一书中的第1章,第1.4节,作者:【美】C.J.Date,更多章节内容可以访问云栖社区“异步社区”公众号查看。
1.4 数据库系统与程序系统
这两个概念好像不太好区别,但是也有一些相似性。事实上,通常不认为一个数据库系统就是一个程序系统(更确切地说,是一种特例)。图 1.3 给出了代码片段,这段代码的目的是要计算一维数组A中整型数据的和,并将其显示在终端上(该片段采用一种假设的语言来表示,但该语言具有自解析性)。
相关说明如下。
语句:整个代码由9条语句组成。在程序设计语言中,一条语句(statement)就是一个指令,它可以引发一些动作,比如,定义或修改一个变量、改变控制流等。通过观察你会发现语句与表达式之间的逻辑差异,表达式就是由一个值构成的(可以认为它是计算或决定值的一种规则)。例如,在图1.3中,“I=I+1”是一条语句(即赋值语句),而I+1是一个表达式。注意,在整本书中,我都采用通用的语法来表示,即语句以分号结束,而表达式不用。
类型:类型(type)就是一组值的集合,即特定类型的所有合法值的集合。任何一个值或者变量都要属于某种类型8。图1.3所示的代码片段涉及3种类型:INTEGER(即所有整数的集合)、ARRAY[1..N] OF INTEGER(即下限为1、上限为N的一维整型数组)、CHAR(所有字符的集合)。注意,就像我们一会儿将要看到的(看下面将要讨论的比较运算符),它也涉及了BOOLEAN,即所有逻辑值的集合。当然,它只有2种取值,即TRUE和FALSE。
变量:变量(variable)就是所有值的容器(通常不同的时候会有不同的值)。图1.3所示的代码片段涉及了4个变量,即I、N、SUM、A。给定的变量的当前值(指的是在特定的时间,这个变量所包含的值)是可以用一种而且只能用一种方法来改变,即执行一条赋值语句,在该语句中把变量作为赋值对象。事实上,变量都是可以被赋值的,而值也都可以被赋予变量。
赋值:赋值(assignment)(比如图1.3中的:=)就是一个运算符,它可以修改一个变量的值。也就是说,给变量赋予一个值,这个值也许和以前的值不相同。图1.3所示的代码片段包含了4条赋值语句。
符号:符号(literal)是一种“自定义符号”。例如,符号可以表示某个值,而这个值的类型就由讨论的符号来决定。图 1.3 所示的代码片段中有3种符号,即0、1和‘The sum is’(前两个都是整型,第三个是字符型)。
值:值(value)指的是单独的一个常量。它通常由表达式来声明,也可以被赋予一个变量。注意在特定情况下,符号和变量引用都是表达式,因此,它们都可以用来声明值。每一个值也表达一种特定的数据类型。
只读运算符:只读运算符(read-only operator)就是一种运算符,比如“+”,它可以修改旧值,得到新值。例如表达式2+3,就从原来的值2和3得到了一个新值5。但要特别注意的是,当它被借用时,只是返回一个结果而不修改任何事物(尤其是不修改它的操作数9)。但还要引起注意的是,我在早前所说的那些就是一种计算值的规则,但表达式可以等价地去表示一种只读运算符的借用。事实上,术语表达式(expression)和只读运算符(read-only operator)借用是可以互换的。最后要注意的是,只读运算符不能使用像“+”这样的专门运算符来声明。事实上,大多数这样的运算符都采用了一些惯常使用的标识符来声明。例如,许多程序设计语言都支持RANDOM这个只读运算符来产生一些随机数。
比较运算符:比较运算符(comparison operator)就是类似于“<”的运算符,当被借用时,返回一个真值(TRUE或者FALSE)。事实上,这样的运算符也是只读运算符,但它们的返回值类型为BOOLEAN。
最后要说明的是,之所以详述这些大家已经相当熟悉的知识,是因为所有以前出现的概念都是和数据库直接相关的,这些在接下来的内容中将会看到。
更多的类型
首先,对于类型的定义,我尤其要多讲一些。图1.3所示的代码片段不能解释这一点,但是通常情况下,类型或者由系统定义,或者由用户定义,它们也可以任意组合。例如,在一些几何应用中,用户也许会定义如下类型:POINT、LINE、RECTANGLE、CIRCLE等。然而,对于只是使用它们的用户(与实际定义它们的用户是相对而言的)来说,这些定义类型的用户更能准确地去认识系统。所以,在这本书后面我所提供的例子中,为了尽量减少失去或不失去通用性,我限定大部分情况下只能定义的类型有INTEGER和CHAR,但不完全是这样。
其次,我曾经说过,每个值都是属于某种类型的。它要把每个变量、每个参数赋予每个运算符、每个只读运算符或者每个表达式(尤其是每个符号和每个变量引用),这些运算符也是属于某种类型的,因为有这些结构存在,所以使用它们时肯定会声明某些值。具体说明如下。
就变量、参数和只读运算符来说,当定义问题中的一些结构时,问题中的类型就要被说明。例如,看图1.3中的变量定义,即VAR语句。
就表达式而言,当要计算问题中的表达式时,它的类型只是返回结果的类型。例如,表达式2+3的类型为INTEGER,这是因为该表达式的结果5是INTEGER。
第三点,也是最后一点,理解与给定类型T的关系,这一点很重要。对于类型T,定义了一组操作符的集合,要操作类型T中的值和变量(因为没有操作符的类型是无用的)。例如,对于类型INTEGER而言,为简单起见,我只定义系统,由代理负责定义类型。换句话说,该系统必须定义如下内容。
为了给整型赋值或者比较整型,必须定义运算符“:=”、“=”、“<”等。
为了在整型上执行算术运算,必须定义运算符“+”、“*”等。
为了把整型转化为字符串,也要定义CAST运算符。
不要定义“||(连接运算符)”、“SUBSTR(求子串)”这样的运算符(至少在我给的例子中没有使用),因为这些运算符对于整型数据操作没有意义。
理解给定类型T中必须包含赋值和等式子、比较是非常重要的,而且,这些运算符的语法必须要满足下述条件。
赋值:把值v赋值给变量V之后,比较V=v就是TRUE。注意,这个条件有时也称为赋值原理。
等式:如果v1和v2具有相同的值,则v1=v2就是TRUE(提示:它们必须要具有相同的类型)。注意下面这条重要推论:如果存在运算符Op,且Op(v1)≠Op(v2),那么v1=v2就是FALSE
。
注意:对于“:=”和“=”这两个运算符,它们的等价性是非常重要的。因为没有它,我们就不能讨论值v和值的集合S,不管v是否出现在S中(例如,判断v是否是S的元素)。
最后,我再重复一下,之所以详细讨论我们已经相当熟悉的知识,是因为所有的定义都是直接与数据库相关的,这一点在下面的章节中将会看到。