供稿人:毕永军
在版本9.1以前,DB2对于XML的支持是通过DB2 XML Extender 提供的。XML的存储通过大对象(LOB)方式或者转换为关系型数据表的方式实现。在处理XML时由于需要做大量的转换工作,性能不能尽如人意。从版本9.1开始,DB2实现XML数据本身特有的层次化存储,从而成为业界第一个内置支持XML和关系型数据的混合数据库软件。你可以通过XQuery与SQL查询的结合,方便地在一条查询语句中查询XML文档和关系型数据表。由于我们对于XML字段提供索引支持和特有的层次化存储方式,DB2可以提供一流的XML查询性能,不仅可以查询整个XML文档,而且可以查询XML文档中的元素(Element)以及属性(Attribute)。下面我们将依次简单介绍XML、XML在DB2中的存储,并通过一个实例讲解如何在DB2版本9.1中操作XML数据。
XML简介:
XML是eXtensible Markup Language(可扩展标记语言)的简称,是一种从通用标记语言SGML(Standard Generalized Markup Language) 发展而来的简单灵活的文本格式。SGML最初设计用来满足大规模电子印刷的需求,现在XML在万维网及其他应用的数据交换中发挥着越来越重要的作用。
XML是一种数据定义和数据内容自包含的文档,也就是说这个文档可以自己解释自己。这使得数据交换变得简单,一个应用只需要根据一个XML文档就可以获得所需的数据和解析数据的方法。下面我们看一个简单的XML文档publications.xml的例子:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<publications>
<textbook title="Programming with XML">
<isbn>0-11-011111-0</isbn>
<author>Mary Brown</author>
<author>Alex Page</author>
<publicationDate>2002</publicationDate>
<university>University of London</university>
</textbook>
<childrensbook title="Children’s Fables">
<isbn>5-55-555555-5</isbn>
<author>Bob Carter</author>
<author>Melaine Snowe</author>
<publicationDate>1999</publicationDate>
</childrensbook>
</publications>
从文档中我们可以看出,XML文件中的标记都用“<> </>”包含起来,这跟HTML的标记方法类似。并且在一个标记中可以嵌入另外一个标记。如上例中,在<publications>…</publications>中嵌套了<textbook>…</textbook>和<childrensbook>…</childrensbook>两个标记。注意,标记之间必须是完全包含的,不能跨越包含。如下面的文档就是一个非法的文档:
<book title = “XML is a good language”>
<isbn>22-022322-23<author>Tom Cruise</isbm></author>
</book>
上例中<isbn>和<author>这两个标记就出现了跨越嵌套的情况,这在XML中是不允许的。
从上述文档的例子中我们不难看出,XML文档可以用一个层次化的树形结构表示。上例中,根结点就是publications,接下来有两个叶子节点textbook和childrensbook,如图 1所示:
图 1 publications.xml的文档示意图
下面简要介绍一下XML文档中的几个重要概念。
1. XML 声明(Declaration)
a) 下面这个标记,就是XML的声明,基本上所有的XML文档都以声明开头。声明制定了解释器处理这个XML文档的一些基本信息。该声明中指定了XML标准的版本信息,当前这个版本信息必须是1.0。encoding指明了这个文档的编码是UTF-8编码,告诉解释器处理后面的字符时,按照UTF-8解码。standalone指定处理该文档时是否需要其他的文件,如果需要就用”no”,否则就用”yes”。
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2. 元素 (Elements)
a) 刚才我们讲到的标记对,如<publications></publications>就是元素,其中每一个文档必须有且仅有一个根元素,如上面的<publications>。元素之间可以互相嵌套,但是不能互相跨越。另外每一个元素必须有结束的标记,如<publications>,必须有一个结束的标记</publications>。注意:XML的元素区分大小写,这和HTML不一样。<publications></publications>和<:p src="http://www.wlkj.net/images/smilies/default/titter.gif" border=0 smilieid="9">UBLICATIONS></PUBLICATIONS>就会被认为是两个不同的元素。
3. 属性(Attributes)
a) 属性包含在元素标记的尖括号中,采用attributes=”content”的方式表示,如上例中的<textbook title="Programming with XML">,其中title就是元素textbook的一个属性,其中一个元素可以有多个属性,每一个属性必须有值,值必须用双引号括起来。
4. XML中的其他内容
a) 注释(Comments):注释内容以<!—开头,并且以-->结尾。如下所示为一个注释:
<!—The next part is for textbook-->
<textbook>……</textbook>
;p
b) 处理指令(Processing Instructions):处理指令是用于特定代码段的。如下所示, Cocoon是Apache软件联盟的XML处理构架,它指出下面的代码部分是SQL语句。
<?Cocoon-process type=”sql”>
c) 实体(Entities):实体类似其他编程语言中的宏,XML解释器处理文档时会把碰到的实体标记替换为实体的实际内容。如下所示,XML解释器会将所有碰到的&dw替换为“developerWorks”。
<!-- Here's an entity: -->
<!ENTITY dw "developerWorks">
另外,XML文档还有命名空间(Namespace)、文档类型定义(Document Type Definitions)和模式(Schema)的支持,在此就不一一介绍了。
关于详细的内容请参见W3C网站:
http://www.w3.org/XML/ 。另外在IBM 开发者园地上有很多关于XML的专题文章,请参见:http://www-128.ibm.com/developerworks/xml 。
XML在DB2中的本地化存储
我们知道XML文档是一种层次化的树形结构,在DB2中存储时,也是按照XML特有的树形结构存储,以便更加有效地操作XML数据。下面是一个XML文档products.xml:
<products>
<product xmlns="http://posample.org" pid="10">
<description>
<name>Fleece jacket</name>
<price>19.99</price>
</description>
</product>
<product xmlns="http://posample.org" pid="11">
<description>
<name>Nylon pants</name>
<price>9.99</price>
</description>
</product>
</products>
图 2所示为DB2中采用的一个简化的数据模型。
图 2 products文档的数据模型
在DB2中,我们采用树形结构表示XML文档,节点代表XML文档中的内容,对应于XML文档中的内容,我们有如下几种节点:
1. 文档节点(D):代表整个文档。
2. 元素节点(E):代表文档中的所有元素。其中每一个元素节点又有如下的属性:
a) 节点名
b) 父节点,可能为空
c) 类型名
d) 子节点,可能为空
e) 属性,可能为空
f) 字符串值
g) 类型值
h) 内含命名空间
3. 属性节点(A):代表文档中的属性。
4. 文本节点(T):代表文档中的字符内容。
5. 处理指令节点:代表文档中的处理指令。
6. 注释节点:代表文档中的注释。
在实际存储中,每一个节点都有自己的唯一标志符。存储的顺序遵循如下规则:
1. 根节点是第一个节点
2. 元素节点出现在子节点的前面
3. 属性节点紧跟着元素节点出现,顺序随意,但是在查询的时候顺序不变。
4. 兄弟节点的顺序取决于在文档中的位置。
5. 每一个元素节点及子节点出现在下一个元素节点的前面。
通过将XML文档解析为树形结构,DB2按照固定的顺序存储XML文档,并能够对XML文档字段中的元素或者属性建立索引,快速检索XML字段。
DB2本地化存储XML数据示例
在DB2中,我们新增加了一个数据类型XML,支持XML文档的存储。下面通过一个例子来演示一下在DB2中如何存取XML数据:
1. 创建一个例子数据库,注意,目前DB2仅在Unicode的数据库中支持XML本地存储。由于在XML文档中会包含“;”,所以在进入命令行处理器时使用“~”做分隔符:
db2 -td~
CREATE DATABASE xmltut USING CODESET UTF-8 TERRITORY US~
2. 连接到数据库:
CONNECT TO xmltut~
3. 创建Customer表:
CREATE TABLE Customer (Cid BIGINT NOT NULL PRIMARY KEY, Info XML)~
4. 在XML字段Info的属性客户Cid上创建一个索引:
CREATE UNIQUE INDEX cust_cid_xmlidx ON Customer(Info)
GENERATE KEY USING XMLPATTERN
’declare default element namespace "http://posample.org"; /customerinfo/@Cid’
AS SQL DOUBLE~
5. 插入三条包含XML的数据,你可以将下面的SQL语句写入一个文件insert.sql中,然后使用db2—td~ -f insert.sql来插入数据:
INSERT INTO Customer (Cid, Info) VALUES (1000,
'<customerinfo xmlns="http://posample.org" Cid="1000">
<name>Kathy Smith</name>
<addr country="Canada">
<street>5 Rosewood</street>
<city>Toronto</city>
<prov-state>Ontario</prov-state>
<pcode-zip>M6W 1E6</pcode-zip>
</addr>
<phone type="work">416-555-1358</phone>
</customerinfo>')~
INSERT INTO Customer (Cid, Info) VALUES (1002,
'<customerinfo xmlns="http://posample.org" Cid="1002">
<name>Jim Noodle</name>
<addr country="Canada">
<street>25 EastCreek</street>
<city>Markham</city>
<prov-state>Ontario</prov-state>
<pcode-zip>N9C 3T6</pcode-zip>
</addr>
<phone type="work">905-555-7258</phone>
</customerinfo>')~
INSERT INTO Customer (Cid, Info) VALUES (1003,
'<customerinfo xmlns="http://posample.org" Cid="1003">
<name>Robert Shoemaker</name>
<addr country="Canada">
<street>1596 Baseline</street>
<city>Aurora</city>
<prov-state>Ontario</prov-state>
<pcode-zip>N8X 7F8</pcode-zip>
</addr>
<phone type="work">905-555-2937</phone>
</customerinfo>')~
6. 更新客户号为1002的用户数据:
UPDATE customer SET info =
'<customerinfo xmlns="http://posample.org" Cid="1002">
<name>Jim Noodle</name>
<addr country="Canada">
<street>1150 Maple Drive</street>
<city>Newtown</city>
<prov-state>Ontario</prov-state>
<pcode-zip>Z9Z 2P2</pcode-zip>
</addr>
<phone type="work">905-555-7258</phone>
</customerinfo>'
WHERE XMLEXISTS (
'declare default element namespace "http://posample.org";
$doc/customerinfo[@Cid = 1002]'
passing INFO as "doc")~
7. 删除客户号为1003的客户记录:
DELETE FROM Customer
WHERE XMLEXISTS (
'declare default element namespace "http://posample.org";
$doc/customerinfo[@Cid = 1003]'
passing INFO as "doc")~
8. 查询表中的XML记录:
a) 使用SQL语句查询所有的记录:
SELECT Cid, Info FROM Customer~
b) 使用SQL语句查询符合XML过滤条件的记录,该查询返回通信地址在Toronto的客户:
SELECT XMLQUERY (
'declare default element namespace "http://posample.org";
for $d in $doc/customerinfo return <out>{$d/name}</out>'
passing INFO as "doc")
FROM Customer as c
WHERE XMLEXISTS (
'declare default element namespace "http://posample.org";
$i/customerinfo/addr[city="Toronto"]' passing c.INFO as "i")~
c) 使用XQuery查询所有的记录:
XQUERY db2-fn:sqlquery ('SELECT Info FROM Customer')~
d) 使用XQuery查询部分符合条件的记录,该查询返回通信地址在Toronto城市的客户:
XQUERY declare default element namespace "http://posample.org";
for $d in db2-fn:xmlcolumn('CUSTOMER.INFO')/customerinfo
where $d/addr/city="Toronto"
return <out>{$d/name}</out>~
通过上面的实例可以看出,在DB2 版本9.1中,我们可以方便地创建包含XML字段的表、插入删除XML文档数据,并通过XQuery和SQL语句的结合查询满足条件的记录。关于XQuery相关的概念和语法,请参见DB2手册《XQeury Reference》:
http://www306.ibm.com/software/data/db2/udb/support/manualsv9.html
参考文献:
1、 DB2手册《XML Guide》:
http://www-306.ibm.com/software/data/db2/udb/support/manualsv9.html
2、 《Introduction to XML》:
http://www-128.ibm.com/developerworks/edu/x-dw-xmlintro-i.html?S_TACT=105AGX06&S_CMP=HP
3、《XQeury Reference》:
http://www-306.ibm.com/software/data/db2/udb/support/manualsv9.html