DB2 将 XQuery 当作最好的语言,允许用户直接编写 XQuery 表达式,而不是将 XQueries 封装或嵌入到 SQL 语句中。而且,DB2 的查询引擎是在本地处理 XQueries。
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. 上述查询的示例输出
这些示例查询的结果都相当简单,因为 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 是在一行中显示每个客户地址记录的。