XML 是一种通用性极强的数据 传送格式,但尽管对它的希望很高,XML 这种格式在数据 存储和 访问方面只达到普通甚至低劣的程度。现在还不是抛弃 (SQL) 关系数据库(为迅速而可靠地查询复杂数据进行了调整)的时候。那么 XML 与关系数据模型之间的关系究竟是怎样的?更具体地说,有什么好的设计方法可以用于同时利用 XML 和关系数据库 -- 包括两者之间的转换 -- 的项目呢?本专栏讨论了由计算机科学家概念化的数据 模型的抽象理论是如何帮助我们开发一些特殊的多表示的数据流。今后的专栏将着眼于帮助进行转换的特定代码和工具;本专栏则侧重于设计注意事项。
我先将 XML 搁置一边来讨论一些抽象的数据模型。一个良好的数据库设计还需要在特定的项目需求级别上对细节做大量工作。我关注比需求更一般的事物:数据模型的 范例。
从广义角度来说,数据库管理系统 (DBMS) 在历史上划分成三种类型:层次、关系和面向对象。一般而言,层次数据库兴起于 60 年代,用在当时的大型机数据处理技术方面。 网络数据库与层次数据库类似,但它们允许多个父/子关系。70 年代,E. F. Codd 与其他一些人通过严格的数学工作创造了现在无处不在的关系数据库模型。80 年代 -- 主要由于面向对象编程的逐渐流行 -- 对象数据库得到了一定程度的普及,特别是所谓的 丰富数据(例如多媒体格式)。
目前,关系数据库管理系统 (RDMS) 在大规模系统的数据存储技术方面仍然占主导地位。层次和对象数据库满足特定需求。不过,许多年来,很多流行的 DBMS 都是对象和关系的混合体。因此在实践当中,数据模型范例之间的界限并不很清晰。
在层次数据库 (HDBMS) 中,首先是从严格定义的数据节点树开始的。每个节点可以包含一些标识数据,以及一组特定子类型的子节点。子节点的数量在同一层上的兄弟之间可以不同,但所有“堂兄弟”的类型都是相同的。图 1 说明了这些关系。
图 1. 假设的层次数据库模型
在层次数据库中,数据访问在结构上是完全可预测的;因此 DBMS 可以高度优化检索和更新。 例如,在图中所示的模型中,您可以使用以下伪查询语法来确定特定书籍的出版商:
清单 1. 图 1 所描述的层次数据库的常规查询
Programming/C.J.Date/An Introduction to Database Systems/Publisher? |
如清单 1 中所示的任何查询都有到指定数据的精确路径,DBMS 可以迅速地在均衡树和字节偏移编码中确定它。您可以使用完全相同的(唯一的)格式来查询所有书籍出版商,只是在类别、作者等方面有所不同。继续基于这个假设的 DBMS 进行讨论,可以编写更一般的、类似于清单 2 中(类似 Python)伪代码的过程化查询。
清单 2. 数据库过程化类型的常规查询
for book in get_children("Programming/C.J.Date"):
print book.field("Title"), book.field("Publisher") |
这时,XML 程序员可能已经注意到伪查询语法看上去非常象 XPath(XML Path 语言),而过程化伪代码看上去则与 DOM(文档对象模型)相当类似。请在查看其它更多模型时记住这一点。
关系数据库由一组表组成,其中每个表都由一个固定的 列(又称为 字段)的集合组成。 每个表中有数量不定的 行(或者 记录)存在。不过,每一行的 主键(一种用于特定一组数据的名称)必须是唯一的。图 2 说明了关系数据库的结构(使用几乎和层次数据库示例中相同的数据)。
图 2. 假设的关系数据库模型
除了具有主键以外,表通常还有一些 辅键。辅键与其它表中的主键对应。 例如在图 2 中,BOOKS 表具有 AuthorID 和 PubID 两个辅键。它们分别充当 AUTHORS 和 PUBLISHERS 表的主键。这里的思想是, BOOKS 的每一行都有一个独一无二的 ISBN 值,每个 AUTHORS 有唯一的 AuthorID,每个 PUBLISHERS 有唯一的 PubID。
您可以对各个表之间的 关系加以约束,例如,可以规定对于 BOOKS 中存在的每一行都必须存在一个 PubID,这个 PubID 对应于 PUBLISHERS 表中某一行的 PubID。如果一个出版商可以通过这种方式“持有”多本书籍,就称之为 一对多关系。另一方面,如果一个作者拥有多本书籍,一本书又有多个作者,就称为 多对多关系。为了更加全面,您也可以定义一个主键必须恰好与一个辅键匹配的 一对一关系。RDBMS 的作用是强化了这些类型的规则。
在关系数据库中,表的设计可能变得非常复杂,包括一些细致的决定。设计过程中主要关心的问题是对表进行适当的 标准化。标准化的目的 -- 用第五“范式”来压缩第一范式 -- 就是除去数据存储方法中的所有冗余。每个非键的数据 只应该位于一个地方。这个目标在层次数据库中几乎是自动完成的,但在关系数据库中需要做些工作。例如, 图 2 所描绘的示例可能有标准化问题。如果书籍有多个作者,那么数据库将第二个作者存储在哪里?唯一的可行的办法是在 BOOKS 中创建额外的一行。但如果这样做的话,只是为了谈及第二个作者,就需要重复相同的 PubID、Date 和 Title。这不仅需要额外的存储空间,而且如果在各行之间 Title 不完全匹配,还会冒引入错误的风险。要解决这个问题,需要重新考虑设计,而这可能涉及创建更多的表和关系。
与层次模型相比,关系模型相当复杂。但复杂性也带来了能力上的大幅增加。 本质上,您可以问 任何有关 RDBMS 的问题,但对于 HDBMS,只能问一些设计到系统中的问题。例如,假设您想知道哪些作者是出生于 1970 年以后的。在上面举例说明的 HDBMS 中,找到答案的唯一方法是搜索 每个书籍叶节点,而这样做的成本极其高。使用 RDBMS,(只需要)使用简单的 SQL 查询,如清单 3 所示。
清单 3. RDBMS SQL 查询
SELECT AuthorName FROM AUTHORS WHERE AuthorBDay > 1970 |
对于更复杂的问题,必须 联接多个表,但标准化允许您以复杂的方法执行。例如,可以使用如下方式查询以上通过 Random House 出版书籍的作者:
清单 4. 查找由 Random House 出版书籍作者的常规查询
SELECT AuthorName FROM AUTHORS a, BOOKS b, PUBLISHERS p WHERE AuthorBDay > 1970 AND a.AuthorID = b.AuthorID AND b.PubID = p.PubID AND p.Publisher = "Random House" |
清单 4 中的查询规定了您希望拥有的几个关系。 可以将它看作是对表的过滤,每个关系都缩小了搜索范围。 不管 RDBMS 要求怎样,都可以在内部实现它们,但请设想以下步骤(与指定查询的顺序反向;但查询条件可以以任何顺序出现):
- 将 PUBLISHERS 范围缩小到仅 "Random House" (PubID 03-4472822)
- 只考虑带有匹配 PubID 的 BOOKS
- 从这些已考虑过的 BOOKS 行中获取属于 AuthorID 的书籍的列表
- 在带有已考虑过 AuthorID 的书籍的 AUTHORS 行中,确定有多少具有符合条件的 AuthorBDay
象清单 4 中的查询所具有的问题是它需要许多步骤,而其中的一些步骤可能涉及到大量资源。在 HDBMS 中即使很简单的一些事情到了 RDBMS 中往往会相对地困难一些。不过,在 HDBMS 中 极其困难的事(几乎非常少)在 RDBMS 中的难度只是 中等。
对象数据库 (ODBMS) 在某些方面退回到了层次模型。ODBMS 中的对象 -- 与面向对象编程语言中的对象非常相象 -- 是许多数据和行为的组合。从这个意义上说,对象与 HDBMS 的分支节点很象,它同样也包含许多子节点。
对象数据库有两个独特的特性:
- 对象可以是异类的,每个对象都可以包含不同的“特有”数据集合
- 对象可以包含一些固有“智能”
图 3. 假设的对象数据库模型
ODBMS 中的异类对象允许每组数据只包含它所需要的内容。为帮助想象这一情形,扩展示例数据库:现在它不只是书库的书籍记录了。图 3 描述的是一个实际的联机书库,它的内容从数据库中表现出来。不同的媒体 -- 录音、电子文本、电影等等 -- 需要不同的描述信息(并包含不同的内容位流)。已知的 ObjectID 指向每个对象,但对象不象在 HDBMS 中那样有严格一致的一组子节点。
因为 ODBMS 中的对象可以包含各种属性和数据,因此查询对象往往通过一组方法来执行。每个对象使用适合于自身的方式实现这些方法。 在图 3 给出的示例中,这两个方法是“总结”和“传送”。书籍的总结可以是其摘要,而电影的总结则可以是电影预告片。每个对象都有必要的智能来确定什么是总结自身的适当方式。另一种考虑对象具有“智能”的方法是从它所携带的元数据方面来说。
“对象数据库管理组 (ODMG)”曾计划过一种称为 OQL 的用于 ODBMS 的标准查询语言(请参阅 参考资料)。
读者已经熟悉 XML 是拼凑起来的,XML 从某种程度上来说是个混合物。XML 可能与数据建模中的对象数据库最为相象,因为它同样也是由节点组成的,并且每个节点都可以包含异类数据。另一方面,节点异类的程度大部分取决于用于定义 XML 文档结构的特定 DTD 或模式。与 XHTML 或 DocBook 有些相象,可以不太夸张地说,几乎任何元素可以出现在几乎任何地方。但 DTD 更多地是面向数据记录,XML 文档和层次数据库可以一样严格。作为一种 传送格式,XML 非常丰富,只要 DTD/模式正确,它足以完整地表示对象或层次结构。
XML 在表示关系数据库方面不太自然。但我应该在此确切地说明一下什么是“不太自然”。XML 的确能够充分地表示来自 RDBMS 的所有事物。您可以直接表示每个表,虽然比实际 RDBMS 的表示要冗长得多。例如,提议用清单 5 中的 DTD 来表示示例数据库中的 BOOKS 表。
清单 5. 表示 BOOKS 表的 DTD
<?xml version="1.0" encoding="UTF-8"?> <!ELEMENT BOOKS (BOOK*)> <!ELEMENT BOOK (ISBN,AuthorID,PubID,Date,Title)> <!ELEMENT ISBN (#PCDATA)> <!ELEMENT AuthorID (#PCDATA)> <!ELEMENT PubID (#PCDATA)> <!ELEMENT Date (#PCDATA)> <!ELEMENT Title (#PCDATA)> |
当然可以使用模式进行更丰富的分类,但这里的核心问题是说将特定的 RDBMS 表表示为 XML 没有任何困难。
您也可以同样轻松地以 XML 表示可能执行的任何特定联接(如 清单 3和 清单 4 中的 SQL 示例所示)。实际上,表示查询结果是对 RDBMS 最重要和最普通的 XML 用法。特定的联系者或请求者通常不需要完整的数据集,而只是其中一些特殊的已过滤和已结构化的部分。SQL 中的 GROUP BY 和 SORT 子句所顾及的比本专栏示例所演示的更具有结构性,但 XML 节点层次结构也可以表示它们的结果。
影响许多“到处 XML”(和“仅 XML”)的因素是在 RDBMS 的核心就是 关系 -- 特别是各个表之间存在的一组约束。约束的实施是使 RDBMS 如此有效和强大的根源。虽然的确有为了沟通目的而以 XML 表示约束集的可能性,但是 XML 没有实施这种约束的固有机制(DTD 和模式是不同的、更有限的约束种类)。如果没有约束,您就只有数据,而没有数据模型(处理事情就有些过于简化了)。
某些 XML 建议者提倡将 RDBMS 类型的约束添加到 XML 中;而其他人则建议以更深奥一些的方式将 XML 构建到 RDBMS 中。我认为这些是从“符合玄妙术语”类型的思考模式中产生的极其不可取的想法。一些主要的 RDBMS 供应商已花费了许多年的努力使关系问题恢复正常,特别是在最大化性能方式方面恢复正常。您无法只是快速地将一组强健而可靠的关系约束添加到实际上与另一种建模范例很接近的 XML 的表示中。而且,XML 的冗长和格式方面的松散从本质上说,与 RDBMS 最大化性能(从较小的范围来说,还有可靠性)所使用的策略是背道而驰的,例如固定的记录长度和紧凑的存储格式。换句话说,可以继续使用 XML 并会为它所许诺的通用数据传送机制而感到兴奋,但请将您的后端数据存放在为它设计的数据库(例如 DB2 或 Oracle)上(对于规模比较小的系统,请保存在 Postgres 或 MySQL 上)。
- 您可以参阅本文在 developerWorks 全球站点上的
英文原文.
- 介绍关系数据模型的原始论文是:
"A Relational Model of Data
for Large Shared Data Banks," E.F. Codd, Comm. ACM 13 (6), June
1970, pp. 377-387。
- 学习关系数据库理论的权威及卓越的参考资料是:
An Introduction to Database Systems (Introduction to Database
Systems, 7th Ed), C. J. Date, Addison-Wesley Pub Co, 1999. ISBN:
0201385902。
- 两种以面向对象方式(以 Python)简化使用 XML 的工具是 David
Mertz 的
xml_objectify和xml_pickle。 有关详细信息,可以阅读他的专栏文章 XML 问题 #1 (xml_objectify) 和 XML 问题 #2(xml_pickle)。 工具本身可以从以下站点下载: xml_pickle和 xml_objectify。 -
对象数据库管理小组 (ODMG)
主页是另一个有关将 XML 用作数据传送格式的信息的资料来源。
- 参阅 David Mertz 以前的“XML 问题”专栏文章:
- XML 问题 #1介绍 Python xml_pickle 对象。
- XML 问题 #2描述如何使用 Python 的 xml_objectify。
- XML 问题 #3介绍 DocBook。
- XML 问题 #4继续介绍如何使用 DocBook 构建旧的文档档案。
- XML 问题 #5说明如何通过 XSLT 将 XML 文档转换成 HTML。
- XML 问题 #6 比较了半打 XML 编辑器,同时特别着眼于那些适合于有大量文本的文档的工具。
- XML 问题 #7 将 DTD 与 XML Schema 进行了权衡,并建议无论 W3C XML Schema 规范有多么成熟,开发者在什么情况下需要坚持使用 DTD。
- 请阅读摘录自 Kevin Williams 所著 Wrox 书籍
ProfessionalXML
Databases的有关
现有数据库的
XML 数据结构的建议。

David Mertz 常常在形式主义和名义主义之间进退维谷。他怀疑精神是本质,但同时也嗤笑突然出现的主干特性。 可以通过 mertz@gnosis.cx 与 David 联系;在 http://gnosis.cx/publish/ 上详细介绍了他的生活。非常欢迎对过去的、这一篇和将来的专栏文章提出建议和意见。