XML 消息和文档中名称空间的广泛使用对应用程序开发人员如何编写查询有很大的影响。不幸的是,名称空间与流行的查询语言(例如 SQL/XML 和 XQuery)之间的关系往往遭到误解。本文通过几个常见的场景,帮助您理解如何查询包含名称空间的 XML 数据。

对某些人而言,XML 名称空间是一剂良药;而对另外一些人而言,XML 名称空间则是一块绊脚石。然而,无论您喜欢它们还是讨厌它们,XML 名称空间无处不在。事实上,很多开发人员认为它们必不可少。

打开一个 Web 服务定义语言(WSDL)文件或 SOAP 消息,将一个字处理文档转换成 XML,或者浏览行业相关的 XML 模式,常常可以发现其中有对多个名称空间的引用。如果想查询 XML 数据,那么就必须考虑这些名称空间,因为名称空间会影响一切东西。(所谓一切东西 是有点儿夸张。不过名称空间对查询的语义的确有着巨大的影响。)

如果忽视了名称空间的存在,那么所编写的查询很可能得不到期望的结果。这算是一个坏消息。好消息是,弄懂如何正确地查询包含名称空间的 XML 数据并不需要花太多时间。本文就可以助您起步。

XML 名称空间简介

XML 名称空间是一项 W3C XML 标准。实际上,XPath、XML Schema、XQuery 和其他 XML 技术都支持名称空间。本文并不提供关于名称空间的详细教程,而只是简述一些关键概念。要了解更多关于 XML 名称空间的知识,请参阅 参考资料 小节。

XML 名称空间允许 XML 文档将来自不同词汇表的元素和属性混合在一起,而不会产生歧义和处理上的冲突。XML 名称空间使得公司可以为他们的 XML 数据使用一个通用的惟一标记词汇表(元素和属性名称),并可以与其他组织共享这个词汇表。有些公司依赖于名称空间来帮助组合来自不同来源的 XML 数据,随业务需求的变化而更新 XML 模式,以及促进文档的重用。

对于应该如何使用名称空间这个问题,很多 IT 专家意见不一。有人提倡在文档和模式中经常、广泛地使用名称空间,而其他人则认为应该慎用或者不用。如果您对这样的争议感兴趣,那么请参阅 参考资料 小节。在本文中我无意介入这场争论。我只准备解释如何查询包含名称空间的 XML 数据,因为您在工作当中很可能会遇到名称空间。

那么,什么是名称空间?名称空间是由一些惟一的 XML 元素名称和属性名称组成的一个集合,这些名称由一个统一资源标识符(Uniform Resource Identifier,URI)标识。这些 URI 常常看上去像是 Web 站点 URL(也就是说,它们包括像 http://www.w3.org 这样的域名),但有时候也使用统一资源名称(Universal Resource Name,URN)。不管是何种情况,URI 并不真正从指定位置检索数据。如果 URI 以 URL 的形式出现,那么甚至不需要引用一个实际的 Web 页面;它可以是一个 “伪” URL,只是作为一个标识符。

XML 名称空间使用 xmlns 关键字来声明。清单 1 显示了用于一个 XML 文档中元素的两个有效名称空间声明。<employee> 有一个基于 URL 的 URI,而 <partner> 元素则有一个基于 URN 的 URI。

清单 1. 示例名称空间声明
. . . 
<employee xmlns="http://www.bogus.com/employee/1.0"/>
<p: xmlns:p="urn:xmlns:bogus:partner1.0"/>
. . .

注意到这两个名称空间声明在指定的 URI 类型上的区别吗?实际上,<partner> 元素包括一个定义且包含一个前缀(在这个例子中为 p)的名称空间。名称空间前缀的使用是可选的,但是这更像是一种风格上的选择。如果一个元素有名称空间前缀,那么它就属于为前缀定义的名称空间;但是,它的子元素除非也加前缀,否则不属于这个名称空间。包含名称空间声明但没有名称空间前缀的元素(例如前一个例子中的 <employee> 元素)属于被声明的名称空间。而它们的子元素除非特别覆盖名称空间,否则也属于这个名称空间。最后,如果无前缀的元素没有显式的名称空间声明,那么将被绑定到作用域内的默认名称空间。如果没有那样的绑定,那么该元素就不属于任何名称空间。

考虑清单 2 中的例子:

清单 2. 一个含多个名称空间的示例 XML 文档
<mydoc>
    <employee xmlns="http://www.bogus.com/employee/1.0">
          <name>John Jones</name>
           . . . 
    <employee>
    <p:partner xmlns:p="urn:xmlns:bogus:partner1.0"/>
           <name>Acme Computer Corp.</name>
            . . . 
     </p:partner>
     <department>
	<name>Sales</name>
	. . . 
      </department>
</mydoc>

在这个例子中,employee 元素的子元素 name 属于 <employee> 元素中声明的默认名称空间(http://www.bogus.com/employee/1.0)。但是,partner 元素的子元素 name 不属于任何名称空间。虽然它是 <partner> 的子元素,但这个 <name> 元素没有继承其父元素的名称空间,因为那个名称空间是用一个前缀声明的。为了使 partner 的 name 信息也被包括在其父元素所在的名称空间内,可以重写这一行,如下所示:

清单 3. 修改后的包括名称空间前缀的元素
             <p:name>Acme Computer Corp.</p:name>

最后,department 元素的子元素 name 不属于任何名称空间。这是因为在 <department> 元素中没有声明名称空间,它不会被绑定到默认名称空间。

可以看到,在同一个文档中混合使用不同形式的名称空间声明很容易产生混乱。通常,如果有机会定义将来要使用的 XML 数据,那么在名称空间的使用上应保持一致性。这样可以简化应用程序和查询。接下来的几个小节将探索名称空间的作用域对查询的影响。

示例环境

在考虑如何查询包含名称空间的 XML 数据之前,需要一些示例数据。为此,我将使用 DB2 V9 来存储和查询关于业务伙伴的 XML 数据,将在一个 PARTNERS 表中维护该数据。如果您打算跟随我进行实践,那么就需要在一个 DB2 UTF-8 数据库中创建该表。(请参阅 “Get off to a fast start with DB2 Viper” 获得关于创建数据库的说明。)

清单 4 中的脚本创建 PARTNERS 表,并插入一些行到该表中(每个行包含一个 XML 文档):

清单 4. 创建一个示例表并为之填充数据的脚本
create table partners (id int primary key not null, status varchar(10), details xml);

insert into partners values (111, 'Gold', 
'<company type="public"> 
  <name>Acme Tech</name>
  <specialty>Technology</specialty> 
  <contact>     
    <name>John Smith</name> 
    <title> VP, Business Development </title>     
    <email>js@us.acme_tech.com</email> 
  </contact> 
</company>');

insert into partners values (200, 'Silver', 
'<company xmlns="urn:xmlns:saracco-sample:company1.0" type="public"> 
  <name>Saturnia Ltd</name> 
  <specialty>Technology</specialty>
  <contact> 
    <name>Klaus Fischer</name> 
    <title>Alliance Manager</title>
    <email>klausfischer@uk.saturnia.com</email> 
  </contact> 
</company>');

insert into partners values (222, 'Gold', 
'<co:company xmlns:co="urn:xmlns:saracco-sample:company1.0" type="private"> 
  <name>Maribel Enterprises</name> 
  <specialty>Public relations</specialty>
  <contact> 
     <name>Maribel Payton</name> 
     <title>CEO</title>
     <email>mpayton@maribelent.com</email> 
  </contact> 
</co:company>');

insert into partners values (333, 'Silver', 
'<co:company xmlns:co="urn:xmlns:saracco-sample:company1.0" type="public"> 
  <co:name>Credo International Corp.</co:name> 
  <co:specialty>Manufacturing</co:specialty> 
  <person:contact xmlns:person=
		"http://www.ibm.com/bogus/saracco-sample/person1.0">         
     <person:name>Helen Rakai</person:name> 
     <person:title>Director of Marketing</person:title> 
     <person:email>helen_rakai@credointcorp.com</person:email> 
  </person:contact> 
</co:company>');

insert into partners values (444, 'Silver', 
'<co:company xmlns:co="urn:xmlns:saracco-sample:company1.0" type="private"> 
  <co:name>Raymond Associates</co:name>   
  <co:specialty>Consulting</co:specialty> 
  <person:contact xmlns:person=
		"http://www.ibm.com/bogus/saracco-sample/person2.0">         
     <person:name>Raymond Sanchez</person:name> 
     <person:title>Dr.</person:title>
     <job:title xmlns:job=
		"http://www.ibm.com/bogus/saracco-sample/job1.0">President</job:title> 
     <person:email>drsanchez@ca.rrs.com</person:email>
    <person:comments>Prefers short-term projects</person:comments>  
  </person:contact>
</co:company>');

这些 XML 文档跟踪关于业务伙伴的相同信息,包括公司名称、行业领域和业务伙伴代表。但是,每个文档在名称空间的使用上不尽相同,有的包含多个名称空间。为了演示名称空间对以后查询的影响,我特意以不同方式使用名称空间。

下面的查询都是从 DB2 命令行处理程序发出的。从 DB2 命令窗口中,发出以下命令来设置一个命令行环境,在此环境中查询语句要以一个百分号(<%>)结束,且 XML 输出以一种易于阅读的方式显示:

清单 5. 设置 DB2 命令行环境
  db2 –td% -i –d

<%> 不是默认的终止符。在命令行环境中,必须覆盖默认的终止符(一个分号,即“;”),因为默认终止符在 XQuery 中是保留的,用于将序言(例如名称空间声明)与查询的主体分隔开。

如果喜欢,也可以使用 DB2 Control Center 的 Command Editor 来发出查询。

在 SQL/XML 和 XQuery 中使用 XPath 表达式

当查询包含名称空间的 XML 数据时,需要在路径表达式中指定适当的名称空间信息。SQL/XML 和 XQuery 都支持允许在 XML 层次间导航的 XPath 表达式。如果您还不熟悉如何在 SQL/XML 和 XQuery 中使用路径表达式,请阅读 “Query DB2 XML data with SQL/XML” 和 “Query DB2 XML data with XQuery”。

现在,让我们来看一些简单的任务,探索如何编写 XQuery 查询来检索所需的信息。我还将展示如何编写符合需求的 SQL/XML 查询。

案例 1:返回所有 XML “company” 数据

第一个任务很简单:检索关于所有业务伙伴的 “company” 数据。如果想获得所有业务伙伴的 XML 数据,那么可以编写一个简单的 SQL 查询:

清单 6. 检索所有公司数据的 SQL 查询
    select details from partners %

但是假设想获得公司数据的一个序列。为此,需要编写一个 XQuery。如果没有使用过名称空间,那么可能习惯性地编写一个这样的查询:

清单 7. 用于检索所有公司数据的不正确的 XQuery
 xquery db2-fn:xmlcolumn('PARTNERS.DETAILS')/company %

然而,这个查询只返回示例表中的一个 XML 文档:

清单 8. 上述 XQuery 的输出
<company type="public">
        <name>
                Acme Tech
        </name>
        <specialty>
                Technology
        </specialty>
        <contact>
                <name>
                        John Smith
                </name>
                <title>
                         VP, Business Development
                </title>
                <email>
                        js@us.acme_tech.com
                </email>
        </contact>
</company>

这是因为该查询中的路径表达式仅指向没有名称空间的 <company> 元素。为了检索所有 <company> 元素,需要重新编写该查询。为了简单起见,可以在路径表达式中使用一个通配符。下面的 XQuery 让 DB2 检索所有文档中作为根节点的 <company> 元素,而不管这些元素的名称空间是什么:

清单 9. 修改后的 XQuery,检索所有公司数据
xquery db2-fn:xmlcolumn('PARTNERS.DETAILS')/*:company %

在 SQL/XML 中,这个查询可以表达为:

清单 10. 用于检索所有公司数据的 SQL/XML 查询
select xmlquery('$c/*:company' passing details as "c") 
from partners  
where xmlexists('$c/*:company' passing details as "c")%

您可能想知道,为什么这个查询中出现了一个 WHERE 子句。严格地说,这不是必需的。因为 PARTNERS 表中的每个 XML 文档都包含一个 <company> 根元素,您只需检索所有公司信息,而不必理会名称空间。然而,如果 PARTNERS.DETAILS 中有一个包含根元素 <firm> 的文档,而在查询中又忽略了上述 WHERE 子句,那么对于那个文档,DB2 返回的是空记录。这是由于 SQL 的语义导致的:没有了 WHERE 子句,SQL 查询就不会在返回的结果集中过滤掉表中的任何行。因此,如果使用 SQL/XML 查询 XML 数据,就必须包括一个 WHERE 子句,并在其中使用 XMLExists() 函数(或其他过滤谓词)来确保结果中不会为表中的每个行都包括一行。

案例 2:返回选定名称空间中的所有 XML “company” 数据

常常需要将对 XML 数据的查询限制在特定的名称空间内。本节考虑如何获得与名称空间 urn:xmlns:saracco-sample:company1.0 相关联的所有有关公司的 XML 文档。

在 DB2 中,必须在查询中声明相关的名称空间。清单 11 为一个 XQuery 表达式声明一个默认的名称空间:

清单 11. 在 XQuery 中声明默认名称空间
xquery declare default element namespace "urn:xmlns:saracco-sample:company1.0";

这个子句不能独立运行。若独立运行则会产生一个 SQL16002N 错误。名称空间声明之后必须紧随着要发出的 XQuery。这个例子声明一个默认的名称空间,并指示 DB2 检索关于与那个名称空间相关联的所有公司的信息:

清单 12. 使用默认名称空间的 XQuery
xquery declare default element namespace "urn:xmlns:saracco-sample:company1.0";
db2-fn:xmlcolumn('PARTNERS.DETAILS')/company%

基于 清单 4 中的内容,DB2 返回由四个 XML 记录组成的一个序列:

清单 13. 上述 XQuery 的输出
<company xmlns="urn:xmlns:saracco-sample:company1.0" type="public">
<name>
	Saturnia Ltd
</name>
<specialty>
	Technology
</specialty>
<contact>
	<name>
		Klaus Fischer
	</name>
	<title>
		Alliance Manager
	</title>
	<email>
		klausfischer@uk.saturnia.com
	</email>
</contact>
</company>
<co:company xmlns:co="urn:xmlns:saracco-sample:company1.0" type="private">
<name>
	Maribel Enterprises
</name>
<specialty>
	Public relations
</specialty>
<contact>
	<name>
		Maribel Payton
	</name>
	<title>
		CEO
	</title>
	<email>
		mpayton@maribelent.com
	</email>
</contact>
</co:company>
<co:company xmlns:co="urn:xmlns:saracco-sample:company1.0" type="public">
<co:name>
	Credo International Corp.
</co:name>
<co:specialty>
	Manufacturing
</co:specialty>
<person:contact xmlns:person="http://www.ibm.com/bogus/saracco-sample/person1.0">
	<person:name>
		Helen Rakai
	</person:name>
	<person:title>
		Director of Marketing
	</person:title>
	<person:email>
		helen_rakai@credointcorp.com
	</person:email>
</person:contact>
</co:company>
<co:company xmlns:co="urn:xmlns:saracco-sample:company1.0" type="private">
<co:name>
	Raymond Associates
</co:name>
<co:specialty>
	Consulting
</co:specialty>
<person:contact xmlns:person="http://www.ibm.com/bogus/saracco-sample/person2.0">
	<person:name>
		Raymond Sanchez
	</person:name>
	<person:title>
		Dr.
	</person:title>
	<job:title xmlns:job="http://www.ibm.com/bogus/saracco-sample/job1.0">
		President
	</job:title>
	<person:email>
		drsanchez@ca.rrs.com
	</person:email>
	<person:comments>
		Prefers short-term projects
	</person:comments>
</person:contact>
</co:company>

结果中不包括关于 Acme Tech 的信息,因为它的 <company> 元素不属于被声明的名称空间。

清单 14 展示了用 SQL/XML 表达上述查询的一种方式:

清单 14. 含默认名称空间的 SQL/XML 查询
select xmlquery('declare default element namespace 
   "urn:xmlns:saracco-sample:company1.0"; 
    $c/company' passing details as "c") 
from partners
where xmlexists('declare default element namespace 
   "urn:xmlns:saracco-sample:company1.0"; 
    $c/company' passing details as "c") %

XMLExists() 函数将结果限制为与指定名称空间相关联的四个公司记录。

案例 3:探索名称空间的大小写敏感性

考虑与 清单 12 中的例子非常相似的一个查询:

清单 15. 名称空间经过修改的 XQuery
xquery declare default element namespace "urn:xmlns:saracco-sample:Company1.0"; 
db2-fn:xmlcolumn('PARTNERS.DETAILS')/company

仔细观察这个查询,其中只有一个字符与前面显示的 XQuery 不同。这个查询中,“Company1.0” 以一个大写字母开头,而前面查询在名称空间的定义中引用的是 “company1.0”。这条查询可以执行成功,但是不返回任何记录。这是因为名称空间和 XPath 表达式一样,是大小写敏感的。

如果查询能够执行,但是没有返回数据,那么请仔细检查查询中的路径表达式和名称空间声明。对于 XQuery 和 SQL/XML 都是如此。

案例 4:声明带前缀的名称空间

到现在为止,前面的例子都是为每个查询声明一个默认名称空间。实际上,也可以用前缀声明一个名称空间,需要的时候可在查询中引用该前缀。如果经常创建包含名称空间前缀的 XML 文档,那么这种方法对于您来说应该很自然了。而且,如果查询需要引用属于不同名称空间的 XML 元素,那么必须使用前缀,在本文的后面可以看到这一点。

下面看看如何重新编写 清单 12 中的 XQuery,在其中使用一个名称空间前缀,而不是使用默认名称空间:

清单 16. 在 XQuery 中使用名称空间前缀
xquery declare namespace x="urn:xmlns:saracco-sample:company1.0"; 
db2-fn:xmlcolumn('PARTNERS.DETAILS')/x:company

类似地,下面看看如何重新编写对应的 SQL/XML,以便在其中使用名称空间前缀:

清单 17. 与前一 XQuery 等效的 SQL/XML
select xmlquery('declare namespace x="urn:xmlns:saracco-sample:company1.0"; 
  $c/x:company' passing details as "c") 
from partners
where xmlexists('declare namespace x="urn:xmlns:saracco-sample:company1.0";  
  $c/x:company' passing details as "c")  %

查询中使用的名称空间前缀可以与数据中使用的前缀不同。需要注意的是,查询中的前缀被绑定到数据中所使用的同一个名称空间 URI。可以在查询中使用一个带前缀的名称空间来检索文档中有默认名称空间的元素,或者进行相反的操作。

案例 5:检索 XML 片段

案例 1 到 4 都是检索 DB2 中存储的整个 XML 文档,实际上往往需要编写只检索文档的某些片段的查询。当然,名称空间的存在对这样的查询也有影响。

考虑这样一个 XQuery,它指示 DB2 检索其中的 <company> 元素及其子元素 <name> 属于一个相同的名称空间(urn:xmlns:saracco-sample:company1.0)的所有业务伙伴的公司名称:

清单 18. 检索属于特定名称空间的公司名称的 XQuery
xquery declare namespace c="urn:xmlns:saracco-sample:company1.0"; 
db2-fn:xmlcolumn('PARTNERS.DETAILS')/c:company/c:name %

对于示例数据,这个查询返回由三个 XML 元素组成的一个序列:

清单 19. 上述 XQuery 的输出
<name xmlns="urn:xmlns:saracco-sample:company1.0">
Saturnia Ltd
</name>
<co:name xmlns:co="urn:xmlns:saracco-sample:company1.0">
Credo International Corp.
</co:name>
<co:name xmlns:co="urn:xmlns:saracco-sample:company1.0">
Raymond Associates
</co:name>

结果中没有包含 Maribel Enterprises 的记录。虽然它的 <company> 元素属于查询中指定的名称空间,但它的 <name> 元素不属于该名称空间。这是因为在表中该记录的 <company> 元素是用一个名称空间前缀定义的。由于它的子节点(包括 <name> 元素)不包含名称空间前缀,因此它们不属于任何名称空间。

下面看看如何用 SQL/XML 表达上述查询:

清单 20. 与上述 XQuery 等效的 SQL/XML
select xmlquery('declare namespace x="urn:xmlns:saracco-sample:company1.0";
    $c/x:company/x:name' passing details as "c") 
from partners
where xmlexists('declare namespace x="urn:xmlns:saracco-sample:company1.0";
    $c/x:company/x:name' passing details as "c") %

案例 6:在一个查询中引用多个名称空间

由于很多 XML 文档包含与不同名称空间相关联的元素,因此有些查询需要引用多个表空间。在这种情况下,只需在查询中声明多个名称空间,然后在需要的时候引用每个名称空间。

考虑下面这个查询,该查询检索不同公司的联系方式信息:

清单 21. 在 XQuery 中使用多个名称空间
xquery declare namespace p="http://www.ibm.com/bogus/saracco-sample/person2.0";
declare namespace c="urn:xmlns:saracco-sample:company1.0"; 
db2-fn:xmlcolumn('PARTNERS.DETAILS')/c:company/p:contact %

对于 清单 4 中的示例数据,DB2 返回一条记录,其中包含 Raymond Sanchez 公司的联系方式信息:

清单 22. 上述 XQuery 的输出
<person:contact xmlns:co="urn:xmlns:saracco-sample:company1.0" xmlns:person=
	"http://www.ibm.com/bogus/saracco-sample/person2.0">
<person:name>
	Raymond Sanchez
</person:name>
<person:title>
	Dr.
</person:title>
<job:title xmlns:job="http://www.ibm.com/bogus/saracco-sample/job1.0">
	President
</job:title>
<person:email>
	drsanchez@ca.rrs.com
</person:email>
<person:comments>
	Prefers short-term projects
</person:comments>
</person:contact>

由于一些原因,其他记录不符合条件:

  • 在 John Smith 的记录中,他的公司或联系方式数据没有相关联的名称空间。
  • Klaus Fischer 的记录使用指定的公司名称空间声明,但他的联系方式数据所属的名称空间不是查询中指定的名称空间。(他的联系方式数据与名称空间 urn:xmlns:saracco-sample:company1.0 相关联。)
  • Maribel Payton 的记录使用指定的公司名称空间声明,但她的联系方式数据不属于任何名称空间。
  • Helen Rakai 的记录使用指定的公司名称空间声明,但她的联系方式数据属于另一个名称空间(http://www.ibm.com/bogus/saracco-sample/person1.0 而不是 http://www.ibm.com/bogus/saracco-sample/person2.0)。

清单 23 展示了用 SQL/XML 表达该查询的一种方法:

清单 23. 与前一 XQuery 等效的 SQL/XML
select xmlquery('declare namespace x="urn:xmlns:saracco-sample:company1.0"; 
   declare namespace y="http://www.ibm.com/bogus/saracco-sample/person2.0";
   $c/x:company/y:contact' passing details as "c") 
from partners
where xmlexists('declare namespace x="urn:xmlns:saracco-sample:company1.0"; 
   declare namespace y="http://www.ibm.com/bogus/saracco-sample/person2.0";
   $c/x:company/y:contact' passing details as "c")

案例 7:关于多个名称空间的更多信息

使用多个名称空间一开始看上去有点儿棘手,我们再来看来一个更复杂一点儿的例子。仔细观察这个查询,看看您是否能理解它的意图:

清单 24. 引用多个名称空间的另一个 XQuery
xquery declare namespace p="http://www.ibm.com/bogus/saracco-sample/person1.0"; 
db2-fn:xmlcolumn('PARTNERS.DETAILS')/*:company/p:contact/title %

这个查询指示 DB2 检索不同公司联系方式的 <title> 元素。它还指定符合条件的 <company> 元素可以与任何名称空间相关联,但 <contact> 元素必须属于 http://www.ibm.com/bogus/saracco-sample/person1.0 名称空间,而且 <title> 元素不能属于任何名称空间。

对于示例数据,该查询不返回任何记录。Helen Rakai 的那条记录也没有返回,因为它包含一个 <company> 根元素,该元素有一个 <contact> 子元素,后者属于指定的名称空间。然而,它的 <title> 元素属于一个特定的名称空间。因此,该记录与查询的过滤标准不匹配。

案例 8:在查询中使用名称空间和属性

案例 1 到 7 涉及 XML 元素节点上的查询。此外还需要考虑如何将名称空间应用于属性。您也许还没有考虑到这种情况。

属性不会继承它们的元素的名称空间,也不会假设一个默认名称空间。如果属性有前缀,那么它属于前缀所指定的名称空间。如果属性没有前缀,那么它就没有名称空间。详细信息请参阅 “W3C Namespaces Recommendation”。当然,在编写查询时,您需要考虑那样的信息。

考虑下面这个 XQuery 例子,它检索公开招股公司的名称:

清单 25. 含有一个名称空间和一个属性的 XQuery 例子
xquery declare namespace c="urn:xmlns:saracco-sample:company1.0";
for $x in db2-fn:xmlcolumn('PARTNERS.DETAILS')/c:company 
where $x/@type="public" 
return $x/c:name %

该查询指定符合条件的 <company><name> 元素必须属于一个特定的名称空间(urn:xmlns:saracco-sample:company1.0)。这个查询中出现的名称空间前缀(c)表明了这一点。注意,@type 表明一家公司是公开招股公司还是私人公司,该属性没有包括名称空间前缀。因此,这个查询产生两条记录:

清单 26. 上述 XQuery 的输出
<name xmlns="urn:xmlns:saracco-sample:company1.0">
Saturnia Ltd
</name>
<co:name xmlns:co="urn:xmlns:saracco-sample:company1.0">
Credo International Corp.
</co:name>

如果在查询中为 @type 属性指定一个名称空间,如清单 27 所示,那么示例 XML 文档中没有符合条件的文档。在这种情况下,DB2 不返回记录:

清单 27. 经过修改的 XQuery
xquery declare namespace c="urn:xmlns:saracco-sample:company1.0"; 
for $x in db2-fn:xmlcolumn('PARTNERS.DETAILS')/c:company 
where $x/@c:type="public" 
return $x/c:name %

如果要使用 SQL/XML 来检索公开招股公司的名称,那么可以编写一个如下所示的查询:

清单 28. 与清单 25 中显示的 XQuery 等效的 SQL/XML
select xmlquery('declare namespace x="urn:xmlns:saracco-sample:company1.0";
 $c/x:company/x:name' passing details as "c") 
from partners
where xmlexists('declare namespace x="urn:xmlns:saracco-sample:company1.0";
 $c/x:company[@type="public"]' passing details as "c") %

案例 9:将 XML 数据转换成关系数据

最后,如何将包含名称空间的 XML 数据转换成关系数据?很多已有的应用程序和商业工具(例如查询/报告编写器)可以用来使用存储在 VARCHAR、INT、DATE 等传统数据类型的列中的数据。因此,程序员可以使用 SQL/XML() 函数动态地将 XML 数据转换成更传统的 SQL 数据类型。

当然,原始 XML 数据中存在的名称空间将对如何编写那样的 “转换” 查询造成一定的影响。考虑下面的例子,该查询检索关于业务伙伴的信息,它的 status 必须为 “Silver”,它的公司数据必须与一个给定的名称空间相关联。该查询返回公司的 ID 和名称,以及公司联系人的姓名和电子邮件地址。

清单 29. 涉及 XMLTable() 和名称空间的 SQL/XML 查询
select p.id, z.company, z.name, z.email
from partners p, 
xmltable('declare namespace ns="urn:xmlns:saracco-sample:company1.0";
 $c/ns:company' passing details as "c" 
 columns 
    "COMPANY" varchar(30) path 
        'declare namespace ns="urn:xmlns:saracco-sample:company1.0";ns:name',
     "NAME" varchar(30) path '*:contact/*:name',
     "EMAIL" varchar(30) path '*:contact/*:email'
) as z
where p.status = 'Silver' %

该查询的有些方面值得注意。首先,这个查询是用 SQL/XML 编写的。这个查询不能用纯 XQuery(不含 SQL)表达出来,因为要返回的数据包括公司 ID,它存储在一个 SQL 整数列中。XQuery 只操作 XML 数据类型,而不能操作关系数据类型。其次,SQL/XML 查询中的每个路径表达式需要一个单独的名称空间声明。因此,在这个例子中,该查询在两个地方为 urn:xmlns:saracco-sample:company1.0 声明了名称空间。最后,符合条件的公司代表的姓名和电子邮件地址可以属于任何名称空间(或者不属于任何名称空间),因此 NAME 和 EMAIL 列的路径表达式不需要声明名称空间。

对于这个查询,DB2 返回一个由三行组成的结果集:

清单 30. 上述 SQL/XML 查询的输出
ID          COMPANY                   NAME                 EMAIL                         
----------- ------------------------- -------------------- ------------------------------
 200        Saturnia Ltd                Klaus Fischer       klausfischer@uk.saturnia.com  
 333        Credo International Corp.   Helen Rakai         helen_rakai@credointcorp.com  
 444        Raymond Associates          Raymond Sanchez     drsanchez@ca.rrs.com

结束语

如果想学习关于 XQuery 与 SQL/XML 的基础以外的更多知识,那么必须理解文档和消息中的 XML 名称空间对查询的语义的影响。否则,就可能收到意外的结果。幸运的是,学习如何查询包含名称空间的 XML 文档并不难。本文提供了一些例子来帮助您起步。

致谢

感谢 Matthias Nicola、Bryan Patterson 和 Bert van der Linden 对本文作了审校。

参考资料

学习

获得产品和技术

  • 使用可直接从 developerWorks 下载的 IBM 试用软件 构建您的下一个开发项目。

讨论

条评论

developerWorks: 登录

标有星(*)号的字段是必填字段。


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件

 


在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。

所有提交的信息确保安全。

选择您的昵称



当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

标有星(*)号的字段是必填字段。

(昵称长度在 3 至 31 个字符之间)

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

 


所有提交的信息确保安全。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Information Management, XML
ArticleID=232573
ArticleTitle=查询包含名称空间的 XML 数据
publish-date=06212007