日期:2013-10-06  浏览次数:20459 次

 
关系的真相

长期以来,我们习惯了称关系型数据库中的表为二维表。由于它有行和列,很容易我们就可以把它同一个二维平面联系起来,但理想上,这并非关系型数据库的初衷,也并非符合关系模型的设计。其实长久以来,我对此也只要一个很模糊的概念,对平面表的观点虽有怀疑,却不断无从验证。直到有一天,翻出一本老书——《关系数据库》(石树刚、郑振楣编著,清华大学出版社,1993年),这本老书没有什么流行的新噱头,却满满当当地净是数学理论。这本书读起来并不是很诱人,不过的确很严谨,澄清了我的很多不明之处。也激励我找来各种权威材料重头学起。薄薄一本书,定价只要9.9元,想想这该当是我在上学时,从旧书摊上买的,可能当时只花了不到五元钱。这肯定是我买的性价比最高的一本书了。

在此,我不想从别人书中抄出大段原话,拼出一篇文字,有这时间还不如上网回贴子更有成就感,我宁愿将我的心得,用并不紧密,但尽可能易懂的言语写下。让朋友们先对关系模型有个基本概念,使众多像我这样非科班出身的读者多少了解一下理想真相,不至于在任务中有所不便。如果你想真正掌握关系型数据库的数学模型,请读一读这本不流行的老书《关系数据库》。当然,还有很多早先的正轨教材也写得很不错,比如如今在我手边的《DATABASE SYSTEM CONCEPTS》(机械工业出版社)和《SQL-3 参考大全》(机械工业出版社)。前两本写得更严谨一些,后一本是译得不太好,有些关键地方读着莫明其妙,不过总得来说还是本难得的好书。

言归正传,如今我们说说关系型数据库到底是怎样一回事。我们先看一个表:

X Y Z

------------------

0 0 0

1 1 1

0 0 1

0 1 1

……

这个表存储一个三维空间内的一些点。我们可以很清楚地看到,每一个完整的行,才代表一个点。仅定位某行某列,它并不能表达这个表所定义的信息结构。当我们向这个表中插入或删除数据,它仍代表三维空间内的点集。但如果我们添加一列T(time)呢?那一切全变了,它不再是三维空间了,如今,这个表中存储的是四维的信息了(读过绝对论的朋友该当了解,绝对论中的时空就是一个四维坐标系)。删除一列呢?比如Z列,如今我们面对的就是X-Y 平面了,这是一个二维坐标系。也就是说,在表中删除或添加或修正若干列,并不会改变这个表所代表的意义。而修正或增删列,就会改变表的结构,表所代表的意义也就不同于以前了。表的行和列,并不是平等的!我们不能简单地把它理解为一个平面!相反,我认为,将表的结构理解为一个代数空间更合适,这样,表中的每一行是这个代数空间中的一个点,那么当前表中的信息就构成了这个空间中的一个点集。当然,这个集合还可有其它的约束条件,下面我们从关系模型说起。

在严厉意义上的关系模型中,一个关系模式,包括关系名、无限属性集、属性值域、属性列到值域的映射、完整性约束和属性间依赖。对应数据库中的结构,通常一个关系模式对应一个或多个表和视图。这些表和视图有其各自包括的列,而列有列名、列的数据类型、表和视图的主键约束、外键约束、检查、断言、触发器(在SQL Server2000中,已可以在视图中定义索引和触发器,再加上视图本身的 SQL脚本,视图以可以定义为一个完整的关系模式)。一个简单的关系模式可以只由一个表构成,而多个元组(表或视图)组成的关系,其互相的关联由外键和触发器来完成。在这个定义中,关系中的各个列(也就是说关系的模式)可以有很强的依赖性。比如某些列有主键约束,另一些列有援用外键。而行与行之间(也就是说关系之间)的依赖只存在于两种情况——外键和触发器。其中外键可以表达为模式上的(也就是列之间的)依赖。例如以下订单系统,它由客户表,订单表和

CREATE TABLE CUSTOMERS(

CUSTOMERID INT NOT NULL,

FIRSTNAME CHAR(20),

LASTNAME CHAR(20),

CITY VARCHAR(30),

CONSTRAINT PK_CUSTOMER PRIMARY KEY(CUSTOMERID)

)

CREATE TABLE GOODS(

ID INT NOT NULL,

NAME CHAR(20) NOT NULL,

NUMBER INT,

PRICE MONEY,

CONSTRAINT PK_GOOD PRIMARY KEY(ID))

CREATE TABLE ORDERS(

ORDERID INT NOT NULL,

CUSTOMERID INT NOT NULL,

ORDERDATE DATETIME,

CONSTRAINT PK_ORDER PRIMARY KEY(ORDERID),

CONSTRAINT FK_CUSTOMER FOREIGN KEY (CUSTOMERID)

REFERENCES CUSTOMERS(CUSTOMERID))

CREATE TABLE ITEMS(

ITEMSID INT NOT NULL,

ORDERID INT NOT NULL,

NUMBER INT NOT NULL

CONSTRAINT PK_ITEM PRIMARY KEY(ITEMSID),

CONSTRAINT FK_GOOD FOREIGN KEY (ITEMSID)

REFERENCES GOODS(ID),

CONSTRAINT FK_ORDER FOREIGN KEY (ORDERID)

REFERENCES ORDERS(ORDERID)

)

以上一表中,一位客户可以有多张订单,订单的订户依赖客户表。一张订单可以有多个条目,明细条目又依赖于订单和商品的存在。这四个表就构成一个关系模式,四个表都有其主键,表中的其它列依赖于主键列。表之间的外键援用则构成了表之间的依赖关系。由此联接起了整个模式。每一个完整的订单,包括ORDERS表中的订购信息和ITEMS表中的明细,构成一个关系。而一个用户和他的订单,又可以构成一种新的关系,这些关系的模式,可以由SQL语句来表达,这里不详细讲了,朋友们可以试一下,很简单的联接查询而已。我们可以看到,这个典型的关系中,信息的内容之复杂,远远超出了“几个二维平面表”所能定义的。“带有强约束条件关联的若干代数空间点集”可能更好一些。甚至,我们还可以在其上定义更强的约束,比如用一个触发器保证用户的订购数在某一范围内。当我们增删订单,存货甚至客户时,由于强无力的约束存在,保证了每个关系仍是完整的。可列的定义本身就是关系模式的一部分,如果我们改变或增删了列,修正的是整个模式。至少一个代数空间,被我们改变了。这也就是行和列的区别。当然,只定义一个表,不给它加以任何约束,在技术上是允许的,但这样的表不能称为一个关系。它甚至不能保证最基本的关系完整性。如以前的文章所示,这样的表轻易就可以插入反复数据,而这些不能互相区别的数据并不能给我们更多的信息。这种没有约束的表在实用中该当严厉限制于临时表,不应在其它任何场合出现。

希望读了这篇文章的朋友,能够不再犯我当初常初的错误,建立一个又一个没有任何约束的表,存入不可靠的数据。我们该当把信息的结构和关系模型直接表达在数据库的设计中,这才是关系型数据库的意义所在。这一次几乎没有可执行的代码,可能读者们会有意见。不过真心祝大家能理解我的意思,在关系型数据库的世界中更轻松自若地任务。如果您有批评、表扬、指点,我在这里专心肠听取,谢谢您的支持。我在书中专注于SQL Server 和InterBase,希望有Oracle或DB2等其它数据库系统的高手与我合作,完成本书的不同版本内容,与我分享劳动的艰苦和成功的喜悦。