内容


使用 XQuery 查询 DB2 XML 数据

Comments

您可能已经听说过 DB2 Viper 的新架构,这种架构支持表格和分层数据结构。实际上,前一篇文章已经总结了 DB2 中新的 XML 特性,描述了如何创建数据库对象和用 XML 数据填充这些数据库对象,并解释了如何使用 SQL 和 SQL/XML 操作 XML 数据。本文继续探索 DB2 XML 功能,这一次将焦点放在 DB2 对 XQuery 的新的支持上。

DB2 将 XQuery 当作最好的语言,允许用户直接编写 XQuery 表达式,而不是将 XQueries 封装或嵌入到 SQL 语句中。而且,DB2 的查询引擎是在本地处理 XQueries,这意味着它在解析、计算和优化 XQueries 之前不必在幕后将它们翻译成 SQL。当然,如果您选择编写同时包括 XQuery 和 SQL 表达式的 “双语(bilingual)” 查询,那么 DB2 同样会处理和优化这些查询。

与 “用 SQL 查询 DB2 XML 数据” 中对 SQL/XML 的描述一样,本文也提到了一些常见的查询任务,并告诉您如何使用 XQuery 来完成这些目标。不过首先我们还是简要地考虑一下 XQuery 与 SQL 的不同之处。

关于 XQuery

XQuery 在很多关键方面都与 SQL 有所不同,这很大程度上是因为这两种语言是针对两种具有不同特征的数据模型而设计的。XML 文档包含层次结构,并且有其固有的顺序。而基于 SQL 的数据库管理系统所支持的表格数据结构是平面的(flat),并且是基于集合的;因此,行之间不存在顺序。

这两种数据模型的不同导致它们各自的查询语言有很多基本的不同。例如,XQuery 支持路径表达式,以允许程序员在 XML 的层次结构中导航,而纯 SQL(没有 XML 扩展)则不支持。XQuery 支持有类型的和无类型的数据,而 SQL 数据总是以指定类型定义的。XQuery 没有 null 值,因为 XML 文档会忽略缺失的或未知的数据。当然,SQL 使用 null 来表示缺失的或未知的数据值。XQuery 返回一系列的 XML 数据,而 SQL 则返回各种 SQL 数据类型的结果集。

这只是 XQuery 和 SQL 之间的基本不同点中的一部分。提供一份详尽的列表超出了本文的范围,不过即将发表的 IBM Systems Journal 将更详细地讨论这些语言的不同。现在我们就探索一下 XQuery 语言的一些基本方面,并看看如何使用它来查询 DB2 Viper 中的 XML 数据。

样本数据库

本文中的查询访问在 “DB2 Viper 快速入门”(developerWorks,2006 年 4 月)中创建的样本表。清单 1 给出了样本数据库中 “items” 和 “clients” 表的定义:

清单 1. 表定义
create table items (
id 		int primary key not null, 
brandname 	varchar(30), 
itemname 	varchar(30), 
sku 		int, 
srp 		decimal(7,2), 
comments 	xml
)
create table clients(
id 		int primary key not null, 
name 		varchar(50), 
status 		varchar(10), 
contactinfo 	xml
)

图 1 中显示了 “items.comments” 列中包含的样本 XML 数据,而 图 2 中则显示了 “clients.contactinfo” 列中包含的样本 XML 数据。随后的例子查询将引用这两个 XML 文档中的一个或两个中的特定元素。

图 1. 存储在 “items” 表的 “comments” 列中的样本 XML 文档
存储在 “items” 表的 “comments” 列中的样本 XML 文档
存储在 “items” 表的 “comments” 列中的样本 XML 文档
图 2. 存储在 “clients” 表的 “contactinfo” 列中的样本 XML 文档
存储在 “clients” 表的 “contactinfo” 列中的样本 XML 文档
存储在 “clients” 表的 “contactinfo” 列中的样本 XML 文档

查询环境

本文中的所有查询都是通过交互方式发出的。这可以通过 DB2 命令行处理器或 DB2 Control Center 的 DB2 Command Editor 来完成。本文中的屏幕图像和说明主要使用后一种方式。(DB2 Viper 还附带了一个基于 Eclipse 的 Developer Workbench,它可以帮助程序员以图形化的方式构造查询。本文不讨论应用程序开发问题和 Developer Workbench。)

要使用 DB2 Command Editor,启动 Control Center,并选择 Tools -> Command Editor。这时将出现如 图 3 所示的窗口。在上面的面板中输入查询,单击左上角的绿色箭头运行该查询,然后可以在下面的面板中或者在 “Query Results” 选项卡中查看输出。

图 3. DB2 Command Editor,可以从 DB2 Control Center 中启动
DB2 Command Editor,可以从 DB2 Control Center 中启动
DB2 Command Editor,可以从 DB2 Control Center 中启动

XQuery 例子

与在 “用 SQL 查询 DB2 XML 数据” 中一样,本文将逐步讲解一些常见的业务场景,并展示如何使用 XQuery 来满足对 XML 数据的请求。本文还探索了需要将 SQL 嵌入在 XQuery 中的更复杂的情景。

XQuery 提供了一些不同类型的表达式,这些表达式可以随意组合。每个表达式返回一系列的值,这些值又可以作为其他表达式的输入。最外面的表达式的结果就是查询的结果。

本文主要讨论两种重要的 XQuery 表达式:“FLWOR” 表达式和路径表达式。FLWOR 表达式非常像 SQL 中的 SELECT-FROM-WHERE 表达式 —— 它用于对由多项组成的一个列表进行迭代,并且可以选择返回通过在每一项上进行计算得到的值。而路径表达式则可以在分层的 XML 元素之间进行导航,并返回在路径末端找到的元素。

与 SQL 中的 SELECT-FROM-WHERE 表达式类似,XQuery FLWOR 表达式可以包含数个以某个关键词开头的子句。在 FLWOR 表达式中,有以下用于作为子句开头的关键字:

  • for:对输入序列进行迭代,依次将一个变量绑定到每个输入项
  • let:声明一个变量并为之赋值,可能是一个包含多项的列表
  • where:指定过滤查询结果的标准
  • order by:指定结果的排序顺序
  • return:定义所返回的结果

XQuery 中的路径表达式由一系列的 “步(step)” 组成,之间以斜杠隔开。在最简单的形式中,每一步在 XML 层次中向下导航,以发现由前一步返回的元素的孩子。路径表达式中的每一步还可以包含一个谓词,用于过滤该步返回的元素,只保留满足某种条件的元素。例如,假设变量 $clients 被绑定到包含 <Client> 元素的 XML 文档的一个列表,则 4 步路径表达式 $clients/Client/Address[state = "CA"]/zip 将返回居住在加利福尼亚的客户的邮政编码。

在很多情况下,可以任意使用 FLWOR 表达式或路径表达式编写查询。

使用 DB2 XQuery 作为顶层查询语言

要在 DB2 Viper 中直接执行 XQuery(而不是将它嵌入在 SQL 语句中),必须以关键字 xquery 作为查询的开头。这个关键字将指示 DB2 调用它的 XQuery 解析器来处理请求。注意,只有在使用 XQuery 作为最外层(顶层)语言的时候才需要这么做。如果是将 XQuery 表达式嵌入在 SQL 中,则不需要在语句之前加上 xquery 关键字。但是,本文使用 XQuery 作为基本语言,因此所有查询之前都加上 xquery

当 XQuery 被作为顶层语言时,它需要一个输入数据的源。XQuery 获得输入数据的一种方式是调用一个名为 db2-fn:xmlcolumn 的函数,调用时带一个参数,表明 DB2 表中 XML 列所在的表名和该列的列名。db2-fn:xmlcolumn 函数返回存储在给定列中的一系列的 XML 文档。例如,下面的查询返回一系列包含客户联系方式信息的 XML 文档:

清单 2. 返回客户联系方式数据的简单 XQuery
xquery db2-fn:xmlcolumn('CLIENTS.CONTACTINFO')

您可能还记得,在我们的数据库模式中(参见 “样本数据库” 小节),我们将那些 XML 文档存储在 “clients” 表的 “contactinfo” 列中。注意,这里的列名和表名是大写的。这是因为表名和列名在被写入到 DB2 的内部编目之前通常要换成大写形式。由于 XQuery 是大小写敏感的,因此小写的表名和列名不能与 DB2 编目中的大写名称相匹配。

检索特定的 XML 元素

首先我们来看一个基本的任务。假设您要检索所有提供了有关传真信息的客户的传真号。清单 3 给出了编写该查询的一种方式:

清单 3. 检索客户传真数据的 FLWOR 表达式
xquery 
for $y in db2-fn:xmlcolumn('CLIENTS.CONTACTINFO')/Client/fax
return $y

第一行指示 DB2 调用它的 XQuery 解析器。接下来的一行指示 DB2 对包含在 CLIENTS.CONTACTINFO 列中的 Client 元素的 fax 子元素进行迭代。每个 fax 元素被依次与变量 $y 绑定。第三行指出在每次迭代中返回 $y 的值。结果为一系列的 XML 元素,如 清单 4 所示:

清单 4. 上述查询的示例输出
<fax>4081112222</fax>
<fax>5559998888</fax>

随便提一下,这里的输出还将包含与本文关系不大的一些信息:XML 版本和编码数据,例如 <?xml version="1.0" encoding="windows-1252" ?>,以及 XML 名称空间信息,例如 <fax xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">。为了使输出更简单,本文省略了这些信息。然而,对于很多 XML 应用程序来说这些信息可能很重要。如果使用 DB2 命令行处理器来运行查询,那么可以使用 -d 选项来省略 XML 声明信息,还可以使用 -i 选项以一种美观的方式打印结果。

清单 3 中显示的查询也可以用一种更简单的三步路径表达式来表达,如 清单 5 所示:

清单 5. 检索客户传真数据的路径表达式
xquery 
db2-fn:xmlcolumn('CLIENTS.CONTACTINFO')/Client/fax

路径表达式的第一步调用 db2-fn:xmlcolumn 函数从 CLIENTS 表的 CONTACTINFO 列获得一个 XML 文档的列表。第二步返回这些文档中的所有 Client 元素,第三步则返回嵌入在这些 Client 元素中的 fax 元素。

如果您无兴趣通过查询获得 XML 片段,而只想要符合条件的 XML 元素值的文本表示,那么可以在 return 子句中调用 text() 函数,如 清单 6 所示:

清单 6. 两个用于检索客户传真数据的文本表示的查询
xquery 
for $y in db2-fn:xmlcolumn('CLIENTS.CONTACTINFO')/Client/fax
return $y/text()
(or)
xquery
db2-fn:xmlcolumn('CLIENTS.CONTACTINFO')/Client/fax/text()

上述查询的输出如 清单 7 所示:

清单 7. 上述查询的示例输出
4081112222
5559998888

这些示例查询的结果都相当简单,因为 fax 元素是基于基本数据类型的。当然,元素也可能基于复杂的数据类型 —— 即包含子元素(或嵌套层次结构)。例如客户联系方式信息中的 Address 元素就是这样。根据 “DB2 Viper 快速入门”(developerWorks,2006 年 4 月)中定义的模式,该元素包含街道地址、门牌号、所在城市、州、国家以及邮政编码。考虑 清单 8 中的 XQuery 将返回什么结果:

清单 8. 检索复杂 XML 类型的 FLWOR 表达式
xquery 
for $y in db2-fn:xmlcolumn('CLIENTS.CONTACTINFO')/Client/Address
return $y

如果您猜到返回的结果是包含 Address 元素及其所有子元素的一系列的 XML 片段,那就对了。清单 9 给出了一个例子:

清单 9. 上述查询的示例输出
<Address>
  <street>5401 Julio Ave.</street>
  <city>San Jose</city>
  <state>CA</state>
  <zip>95116</zip>
</Address>
. . . 
<Address>
  <street>1204 Meridian Ave.</street>
  <apt>4A</apt>
  <city>San Jose</city>
  <state>CA</state>
  <zip>95124</zip>
</Address>

注意: 为了易于阅读,这里的示例输出作了格式上的调整。DB2 Command Editor 是在一行中显示每个客户地址记录的。

过滤 XML 元素值

您可以修改上述 XQuery 例子,缩小选择范围。例如,我们来看看如何返回居住在邮政编码为 95116 的地区的所有客户的邮递地址。

与您想像的一样,通过 XQuery where 子句可以根据 XML 文档中 zip 元素的值来过滤结果。清单 10 说明了如何在 清单 8 中的 FLWOR 表达式中添加一个 where 子句,以获得您感兴趣的地址信息:

清单 10. 带有 “where” 子句的 FLWOR 表达式
xquery 
for $y in db2-fn:xmlcolumn('CLIENTS.CONTACTINFO')/Client/Address
where $y/zip="95116"
return $y

这里添加的 where 子句很容易理解。for 子句依次将变量 $y 绑定到每个地址。where 子句包含一个小型的路径表达式,该表达式从每个地址向下定位到其内嵌的 zip 元素。只有当这个 zip 元素等于 95116 时,where 子句才为 true(相应的地址被保留)。

通过在路径表达式中添加一个谓词也可以得到相同的结果,如 清单 11 所示:

清单 11. 带附加过滤谓词的路径表达式
xquery
db2-fn:xmlcolumn('CLIENTS.CONTACTINFO')/Client/Address[zip="95116"]

当然,您可以根据邮政编码的值进行过滤,返回与街道地址无关的元素。而且,还可以在单个查询中根据多个 XML 元素值进行过滤。下面的查询返回居住在纽约市具有特定邮政编码(10011)的地区或圣何塞(San Jose)任何地方的客户的电子邮件信息。

清单 12. 在 FLWOR 表达式中根据多个 XML 元素值过滤
xquery 
for $y in db2-fn:xmlcolumn('CLIENTS.CONTACTINFO')/Client
where $y/Address/zip="10011" or $y/Address/city="San Jose"
return $y/email

注意,我们更改了 for 子句,从而将变量 $y 绑定到 Client 元素,而不是绑定到 Address 元素。这样一来便可以根据 Client 元素的一部分子树(Address)过滤该元素,而返回另一部分子树(email)。where 子句和 return 子句中的路径表达式必须相对被绑定到变量(这里是 $y)的元素来编写。

通过路径表达式可以更简单地表达相同的查询:

清单 13. 使用路径表达式根据多个 XML 元素值进行过滤
xquery 
db2-fn:xmlcolumn('CLIENTS.CONTACTINFO')/Client[Address/zip="10011"  
or Address/city="San Jose"]/email;

仔细观察该查询的两种不同形式,其中比较隐蔽的一点是,与 SQL 程序员的预期相比,返回的结果在两个方面存在不同之处:

  1. 返回的结果中不包含那些符合条件但是没有提供电子邮件地址的客户的 XML 数据。换句话说,如果有 1000 个客户居住在圣河塞或邮政编码为 10011 的地区,其中有 700 个客户提供了电子邮件地址,那么返回的将是这 700 个电子邮件地址。这是由于前面提到的 XQuery 与 SQL 之间存在的基本差异 —— XQuery 不使用 null。
  2. 您无法知道哪些电子邮件地址来自同一个 XML 文档。换句话说,如果有 700 个居住在圣河塞或邮政编码为 10011 的地区的客户,并且每个客户提供了两个电子邮件地址,那么返回的结果是 1400 个 email 元素组成的列表。您得到的不是 一个包含 700 个记录、每个记录由两个电子邮件地址组成的序列。

这两点在某些情况下是可以的,在另外一些情况下又可能不可取。例如,如果需要通过电子邮件将一个通知发送给每个符合条件的有记录的帐户,那么很容易在应用程序中对 XML 格式的客户电子邮件地址列表进行迭代。然而,如果对每个客户只发送一次通知,包括那些没有提供街道地址的客户,那么上述 XQuery 就不能满足要求了。

有多种方法来修改这个查询,使返回的结果以某种方式表示缺失的信息,并且在有多个电子邮件地址来自相同客户记录(即相同的 XML 文档)的情况下作出说明。让我们简要地探索一下其中一种方法。不过,如果只是要检索一个列表,其中对于每个符合条件的客户包含一个电子邮件地址,那么只需对之前查询中的 return 子句略作修改:

清单 14. 只检索每个客户的第一个 email 元素
xquery 
for $y in db2-fn:xmlcolumn('CLIENTS.CONTACTINFO')/Client
where $y/Address/zip="10011" or $y/Address/city="San Jose"
return $y/email[1]

该查询导致 DB2 返回它在每个符合条件的 XML 文档(客户联系方式记录)中找到的第一个电子邮件元素。如果对于一个符合条件的客户,DB2 没有找到电子邮件地址,那么对于这个客户就不返回任何信息。

转换 XML 输出

XQuery 的一个强大的方面是可以将 XML 输出从一种格式转换成另一种格式。例如,可以使用 XQuery 检索所有或部分存储的 XML 文档,并将输出转换成 HTML,以便在 Web 浏览器中显示。下面 清单 15 中的查询检索客户的地址,按照邮政编码对结果排序,并将输出转换成 XML 元素,作为一个无序的 HTML 列表中的一部分:

清单 15. 查询 DB2 XML 数据并以 HTML 格式返回结果
xquery 
<ul> {
for $y in db2-fn:xmlcolumn('CLIENTS.CONTACTINFO')/Client/Address
order by $y/zip
return <li>{$y}</li> 
} </ul>

该查询首先以 xquery 关键字开头,告诉 DB2 解析器 XQuery 是顶层语言。第二行将表示无序列表的 HTML 标记(<ul>)包括在结果中。它还包含本查询中使用的一对花括号中的左括号。花括号指示 DB2 计算和处理其中的表达式,而不是将其当作文字字符串。

第三行对客户地址进行迭代,依次将变量 $y 绑定到每个 address 元素。第四行包括一个新的 order by 子句,指出结果必须按照客户邮政编码(绑定到 $y 的每个 address 元素的 zip 子元素)升序排列。return 子句表明 Address 元素在返回之前要用 HTML 列表 item 标记括起来。最后一行结束查询,并结束 HTML 无序列表标记。

输出将类似 清单 16 所示:

清单 16. 上述查询的示例 HTML 输出
<ul>
  <li>
     <Address>
         <street>9407 Los Gatos Blvd.</street>
         <city>Los Gatos</city>
         <state>CA</state>
         <zip>95032</zip>
     </Address>
  </li>
  <li>
     <Address>
         <street>4209 El Camino Real</street>
         <city>Mountain View</city>
         <state>CA</state>
        <zip>95033</zip>
     </Address>
  </li>
. . . 
</ul>

我们来考虑之前遇到的一个话题:当需要在返回结果中表明缺失的值,以及表明单个 XML 文档(例如单个客户记录)包含重复的元素(例如多个 email 地址)时,如何编写 XQuery。一种方法是将返回的输出封装在一个新的 XML 元素中,如下面 清单 17 中的查询所示:

清单 17. 在 XQuery 结果中表明缺失的值和重复的元素
xquery 
for $y in db2-fn:xmlcolumn('CLIENTS.CONTACTINFO')/Client
where $y/Address[zip="10011"] or $y/Address[city="San Jose"]
return <emailList> {$y/email} </emailList>

该查询将返回一个 “emailList” 元素序列,每个符合条件的客户记录对应一个元素。每个 emailList 元素将包含 e-mail 数据。如果 DB2 在客户的记录中只发现一个 e-mail 地址,它将返回那个元素和它的值。如果 DB2 发现多个 e-mail 地址,它将返回所有 e-mail 元素和它们的值。最后,如果 DB2 没有发现 e-mail 地址,那么它将返回一个空的 emailList 元素。因此,输出如下所示:

清单 18. 上述查询的示例输出
<emailList>
   <email>love2shop@yahoo.com</email>
</emailList>
<emailList/>
<emailList>
   <email>beatlesfan36@hotmail.com</email>
   <email>lennonfan36@hotmail.com</email>
</emailList>       
. . .

使用条件逻辑

XQuery 的 XML 输出转换功能可以与它内置的对条件逻辑的支持相结合,以减少应用程序代码的复杂性。“items” 表中包括一个 XML 列,该列包含客户对产品作出的评论。有些客户要求对他们的评论作出响应,对于这些用户,需要创建新的 “action” 元素,其中包含产品 ID、客户 ID 和评语,以便将这些信息发送给适当的个人进行处理。但是,那些不要求响应的评论在商业上也是非常重要的信息,您不想忽视它们。所以,创建一个 “info” 元素,其中只包含产品 ID 和评语。下面展示了如何使用 XQuery if-then-else 表达式来完成这项任务:

清单 19. 在 XQuery 中使用 “if-then-else” 表达式
xquery 
for $y in db2-fn:xmlcolumn('ITEMS.COMMENTS')/Comments/Comment 
return ( 
	if ($y/ResponseRequested = 'Yes') 
		then <action>
			{$y/ProductID, 
			 $y/CustomerID, 
			 $y/Message}
		      </action>
		else ( <info>
			{$y/ProductID, 
			 $y/Message}
			</info>
		)
)

现在您应该对这个查询的大部分感到熟悉,所以我们只关注条件逻辑。if 子句判断一条给定评论的 ResponseRequested 子元素的值是否等于 “Yes”。如果是,那么执行 then 子句,导致 DB2 返回一个新元素(“action”),其中包含三个子元素:ProductID、CustomerID 和 Message。否则,执行 else 子句,DB2 返回一个 “info” 元素,其中只包含产品 ID 和评语数据。

使用 “let” 子句

您已经看到了如何使用 FLWOR 表达式的所有部分,除了一个部分:let 子句。该子句用于将一个值(可能包含一个由多项组成的列表)赋给一个变量,这个变量可以在 FLWOR 表达式的其他子句中使用。

假设您想得到一份列表,统计出每种产品收到的评论数量。那么可以使用以下查询:

清单 20. 使用 “let” 子句
xquery
for $p in distinct-values
     (db2-fn:xmlcolumn('ITEMS.COMMENTS')/Comments/Comment/ProductID)
let $pc := db2-fn:xmlcolumn('ITEMS.COMMENTS')
        /Comments/Comment[ProductID = $p]
return
   <product>
          <id> { $p } </id> 
          <comments> { count($pc) } </comments>
   </product>

for 子句中的 distinct-values 函数返回一个列表,其中包含在 ITEMS 表的 COMMENTS 列中的所有 Comment 中发现的所有不同的 ProductID 值。for 子句依次将变量 $p 绑定到每个 ProductID 值。对于 $p 的每个值,let 子句再次扫描 ITEMS 列,并将变量 $pc 绑定到一个列表,列表中包含所有 ProductID 与 $p 中的 ProductID 相匹配的评论。return 子句为每个不同的 ProductID 值构造一个新的 “product” 元素。每个 “product” 元素包含两个子元素:一个包含 ProductID 值的 “id” 元素,和一个包含给定产品上所收到的评论的数量的 “comments” 元素。

这个例子查询的结果如下:

清单 21. 上述查询的示例输出
<product>
     <id>3926</id>
     <comments>28</comments>
</product>
<product>
      <id>4097</id>
      <comments>13</comments>
</product>

带嵌入式 SQL 的 XQueries

至此,您已经看到了如何编写 XQuery 来检索 XML 文档片段,创建新格式的 XML 输出,以及根据查询中指定的条件返回不同的输出。简言之,您已经学会了使用 XQuery 查询存储在 DB2 中的 XML 数据的几种方法。

显然,除了本文介绍的内容外,关于 XQuery 还有更多要学的东西。但是我们不能忽略了我们还没有讲到的一个大话题:如何在 XQuery 中嵌入 SQL。如果需要编写根据 XML 和非 XML 列的值过滤数据的查询,那么这样做很有用。

您可能还记得,文章 “用 SQL 查询 DB2 XML 数据” 描述了如何将简单的 XQuery 表达式嵌入在 SQL 语句中,以便完成这样的任务。这里,我们来看看如何反过来做:将 SQL 嵌入在 XQuery 中,从而根据传统的 SQL 数据值和特定的 XML 元素值对结果进行限制。

您可以使用 db2-fn:sqlquery 函数替代 db2-fn:xmlcolumn 函数,后者返回一个表的一个列中所有 XML 数据,而前者执行一个 SQL 查询,并且只返回所选择的数据。传递给 db2-fn:sqlquery 函数的 SQL 查询必须返回 XML 数据。然后 XQuery 可以进一步处理这种 XML 数据。

清单 22 中的查询检索关于评论的信息,评论所涉及的产品是建议零售价("srp")大于 $100 的产品,并且在评论中客户请求响应。您应该记得,价格数据存储在一个十进制数类型的 SQL 列中,而客户评论则存储为 XML。对于存储在数据库中的每条符合条件的评论,返回的数据(包括产品 ID、客户 ID 和客户评论)被包括在一个 XML “action” 元素中。

清单 22. 将 SQL 嵌入在 XQuery 中
xquery 
for $y in 
db2-fn:sqlquery('select comments from items where srp > 100')/Comments/Comment 
where $y/ResponseRequested="Yes"
return (
   <action>
          {$y/ProductID, 
           $y/CustomerID, 
           $y/Message}
  </action>
)

同样,这个查询的大部分对于您来说应该比较熟悉了,所以我们只关注这里出现的新函数:db2-fn:sqlquery。DB2 处理提供给该函数的 SQL SELECT 语句,以确定哪些行包含关于定价超过 $100 的产品的信息。存储在这些行中的文档作为一个路径表达式的输出,该路径表达式返回所有内嵌的 Comment 元素。该查询中随后的部分使用 XQuery where 子句进一步过滤返回的数据,并将选中的评论的一些部分转换成新的 XML 片段。

清楚这些之后,让我们考虑如何解决一个稍微不同的问题。假设您需要一份列表,其中包含居住在圣河塞的 “Gold” 客户的所有 e-mail 地址。而且,如果一个客户有多个 e-mail 地址,那么您希望将这些 e-mail 地址都包括在输出中,作为单个客户记录的一部分。最后,如果某个符合条件的 “Gold” 客户没有提供 e-mail 地址,那么需要检索他或她的邮递地址。清单 23 展示了编写该查询的一种方法:

清单 23. 将 SQL 嵌入在包含条件逻辑的 XQuery 中
xquery 
for $y in 
db2-fn:sqlquery('select contactinfo from clients where status=''Gold'' ')/Client
where $y/Address/city="San Jose"
return (
     if ($y/email) then <emailList>{$y/email}</emailList>
     else $y/Address   
)

这个查询有两个方面要解释一下。首先,嵌入在第二行的 SELECT 语句包含一个基于 “status” 列的查询谓词,它将这个 VARCHAR 列与字符串 "Gold" 相比较。在 SQL 中,这样的字符串放在单引号里面。注意,虽然这个例子看上去使用了双引号,但实际上它是在比较值("Gold")前后使用了两个单引号。“额外” 的那个单引号是换码符。如果在基于字符串的查询谓词周围使用双引号,而不是单引号,就会收到语法错误。

此外,这个查询中的 return 子句包含一个条件逻辑,用于判断给定客户的记录中是否存在 e-mail 元素。如果存在,那么该查询将返回一个新的 “emailList” 元素,其中包含客户的所有 e-mail 地址(也就是那个客户的所有 e-mail 元素)。如果不存在,那么该查询将返回客户的邮递地址(也就是那个客户的 Address 元素)。

索引

最后,值得注意的是,您可以专门创建 XML 索引来加快对存储在 XML 列中的数据的访问速度。由于本文只是一篇介绍性的文章,而且样本数据偏小,因此这里不打算就此话题展开讨论。但是在生产环境中,定义适当的索引对于取得最佳性能是非常关键的。请参阅 参考资料 了解关于 DB2 新的索引技术的更多信息。

结束语

XQuery 与 SQL 之间有一些很大的不同点,本文谈到了其中的一些不同点。了解更多关于该语言的知识将有助于判断该语言在什么情况下对您的工作最有好处,并且有助于理解在什么情况下将 XQuery 与 SQL 相结合会比较有用。在将来的文章中,我们将深入研究另一个有趣的话题:如何开发利用 DB2 XML 功能的 Java 应用程序。本文提供了一个 简单的 Java 例子,其中描述了如何在 Java 应用程序中嵌入 XQuery。

致谢

感谢 George Lapis、Matthias Nicola 和 Gary Robinson 审校了本文。


相关主题


评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Information Management, XML
ArticleID=110935
ArticleTitle=使用 XQuery 查询 DB2 XML 数据
publish-date=05082006