这些示例查询的结果都相当简单,因为 fax 元素是基于基本数据类型的。当然,元素也可能基于复杂的数据类型 —— 即包含子元素(或嵌套层次结构)。例如客户联系方式信息中的 Address 元素就是这样。根据 “DB2Viper 快速入门”(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. 上述查询的示例输出
5401 Julio Ave.
San Jose
CA
95116
. . .
1204 Meridian Ave.
4A
San Jose
CA
95124
注意: 为了易于阅读,这里的示例输出作了格式上的调整。DB2Command 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 程序员的预期相比,返回的结果在两个方面存在不同之处:
返回的结果中不包含那些符合条件但是没有提供电子邮件地址的客户的 XML 数据。换句话说,如果有 1000 个客户居住在圣河塞或邮政编码为 10011 的地区,其中有 700 个客户提供了电子邮件地址,那么返回的将是这 700 个电子邮件地址。这是由于前面提到的 XQuery 与 SQL 之间存在的基本差异 —— XQuery 不使用 null。
您无法知道哪些电子邮件地址来自同一个 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. 查询 DB2XML 数据并以 HTML 格式返回结果
xquery
{
for $y in db2-fn:xmlcolumn(‘CLIENTS.CONTACTINFO’)/Client/Address
order by $y/zip
return
}
该查询首先以 xquery 关键字开头,告诉 DB2解析器 XQuery 是顶层语言。第二行将表示无序列表的 HTML 标记(
)包括在结果中。它还包含本查询中使用的一对花括号中的左括号。花括号指示 DB2计算和处理其中的表达式,而不是将其当作文字字符串。
第三行对客户地址进行迭代,依次将变量 $y 绑定到每个 address 元素。第四行包括一个新的 order by 子句,指出结果必须按照客户邮政编码(绑定到 $y 的每个 address 元素的 zip 子元素)升序排列。return 子句表明 Address 元素在返回之前要用 HTML 列表 item 标记括起来。最后一行结束查询,并结束 HTML 无序列表标记。
输出将类似 清单 16 所示:
清单 16. 上述查询的示例 HTML 输出
9407 Los Gatos Blvd.
Los Gatos
CA
95032
4209 El Camino Real
Mountain View
CA
95033
. . .
我们来考虑之前遇到的一个话题:当需要在返回结果中表明缺失的值,以及表明单个 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” 元素序列,每个符合条件的客户记录对应一个元素。每个 emailList 元素将包含 e-mail 数据。如果 DB2在客户的记录中只发现一个 e-mail 地址,它将返回那个元素和它的值。如果 DB2发现多个 e-mail 地址,它将返回所有 e-mail 元素和它们的值。最后,如果 DB2没有发现 e-mail 地址,那么它将返回一个空的 emailList 元素。因此,输出如下所示:
清单 18. 上述查询的示例输出
love2shop@yahoo.com
beatlesfan36@hotmail.com
lennonfan36@hotmail.com
. . .
使用条件逻辑
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
{$y/ProductID,
$y/CustomerID,
$y/Message}
else (
{$y/ProductID,
$y/Message}
)
)
现在您应该对这个查询的大部分感到熟悉,所以我们只关注条件逻辑。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
{ $p }
{ count($pc) }
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. 上述查询的示例输出
3926
28
4097
13
带嵌入式 SQL 的 XQueries
至此,您已经看到了如何编写 XQuery 来检索 XML 文档片段,创建新格式的 XML 输出,以及根据查询中指定的条件返回不同的输出。简言之,您已经学会了使用 XQuery 查询存储在 DB2中的 XML 数据的几种方法。
显然,除了本文介绍的内容外,关于 XQuery 还有更多要学的东西。但是我们不能忽略了我们还没有讲到的一个大话题:如何在 XQuery 中嵌入 SQL。如果需要编写根据 XML 和非 XML 列的值过滤数据的查询,那么这样做很有用。
您可能还记得,文章 “用 SQL 查询 DB2XML 数据” 描述了如何将简单的 XQuery 表达式嵌入在 SQL 语句中,以便完成这样的任务。这里,我们来看看如何反过来做:将 SQL 嵌入在 XQuery 中,从而根据传统的 SQL 数据值和特定的 XML 元素值对结果进行限制。
您可以使用 db2-fn:sqlquery 函数替代 db2-fn:xmlcolumn 函数,后者返回一个表的一个列中所有 XML 数据,而前者执行一个 SQL 查询,并且只返回所选择的数据。传递给 db2-fn:sqlquery 函数的 SQL 查询必须返回 XML 数据。然后 XQuery 可以进一步处理这种 XML 数据。
清单 22 中的查询检索关于评论的信息,评论所涉及的产品是建议零售价("srp")大于 0 的产品,并且在评论中客户请求响应。您应该记得,价格数据存储在一个十进制数类型的 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 (
{$y/ProductID,
$y/CustomerID,
$y/Message}
)
同样,这个查询的大部分对于您来说应该比较熟悉了,所以我们只关注这里出现的新函数:db2-fn:sqlquery。DB2处理提供给该函数的 SQL SELECT 语句,以确定哪些行包含关于定价超过 0 的产品的信息。存储在这些行中的文档作为一个路径表达式的输出,该路径表达式返回所有内嵌的 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
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 相结合会比较有用。在将来的文章中,我们将深入研究另一个有趣的话题:如何开发利用 DB2XML 功能的 Java 应用程序。本文提供了一个 简单的 Java 例子,其中描述了如何在 Java 应用程序中嵌入 XQuery。
我们一直都在努力坚持原创.......请不要一声不吭,就悄悄拿走。
我原创,你原创,我们的内容世界才会更加精彩!
【所有原创内容版权均属TechTarget,欢迎大家转发分享。但未经授权,严禁任何媒体(平面媒体、网络媒体、自媒体等)以及微信公众号复制、转载、摘编或以其他方式进行使用。】
微信公众号
TechTarget
官方微博
TechTarget中国
相关推荐
-
Google BigQuery使用SQL在云端处理大数据
Google正在建设在云中分析大数据的服务被称为BigQuery。那么Google BigQuery使用SQL在云端处理大数据的优势在哪里?
-
如何用XUL进行XQuery语言编写?
自从和XML数据库合作以来,XQuery获得了巨大的牵引力,但其最薄弱的一环就是写入数据库还有很大局限性。应用程序有时必须创建多种XML数据库或者写回整个图表才能进行简单的数据变化。
-
超越传统:NoSQL数据库大比拼
在两三年前,选择数据库是一件非常容易的事。资金充足的企业会选择甲骨文数据库,而预算不足企业则会选择MySQL。不过,如今的情况已经大不相同了。
-
XQuery:数据集成领域的精耕细作者
XQuery标准的发展历程对我们很有启发意义,TT SOA多年以来对此也有详细描述。XQuery诞生于XML和Web服务的早期,目的是向刚开始使用XML数据……