电脑技术学习

用 SQL 查询 DB2 XML 数据(1)

dn001
  DB2® Viper 正处于 beta 阶段,它为存储、治理和查询 XML 数据提供了新的支持。在本文中,您将学习如何使用 SQL 和 SQL/XML 查询存储在 XML 列中的数据。后续的文章将阐述如何使用 DB2 支持的一种新语言 XQuery 来查询 XML 数据。  虽然 DB2 的混合体系结构与之前的版本有很大的不同,但是要利用它的新 XML 功能并不难。假如您已经熟悉 SQL,那么很快就可以将这方面的技能转化到对存储在 DB2 中的本地 XML 数据的处理上。通过本文就可以知道如何实现这一点。  DB2 Viper(现在处于 beta 阶段)中的 XML 特性包括新的存储治理、新的索引技术以及对查询语言的支持。在本文中,学习如何使用 SQL 或 带 XML 扩展的 SQL(SQL/XML)查询 DB2 XML 列中的数据。接下来的文章将讨论 DB2 中新引入的对新兴的业界标准 XQuery 的支持,并探索 XQuery 在什么时候最有用。  您也许会感到惊奇,DB2 还支持双语查询 —— 即组合了来自 SQL 和 XQuery 的表达式的查询。至于应该使用哪种语言(或两种语言结合使用)取决于应用程序的需要,同时也取决于您本身所把握的技能。其实,将两种查询语言中的元素组合到一个查询中并没有您想像的那么难。这样做还可以为搜索和集成传统 SQL 和 XML 数据提供强大的能力。  Sample 数据库  本文中的查询将访问在 “DB2 Viper 快速入门(developerWorks,2006 年 4 月)中创建的 sample 数据库。这里我们简短地回顾一下,sample 数据库中 "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
)
12345678910下一页   图 1 显示了 "items.comments" 列中的示例 XML 数据,图 2 显示了 "clients.contactinfo" 列中的示例 XML 数据。随后的查询例子将引用其中某个 XML 文档或这两个文档中某些特定的元素。  图 1. 存储在 "items" 表 "comments" 列的示例 XML 文档    图 2. 存储在 "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 启动    纯 SQL 查询  即使您对 SQL 所知有限,也仍然可以很轻松地查询 XML 数据。例如,下面的查询选择 "clients" 表中的全部内容,包括存储在 "contactinfo" 列的 XML 信息: 上一页1234567下一页   清单 2. 简单的 SELECT 语句select * from clients  当然也可以编写更具选择性的 SQL 查询,使之包含关系投影和限制操作。下面的查询检索所有具有 "Gold" 状态的客户的 ID、姓名和联系方式。请注重,"contactinfo" 列包含 XML 数据,而其他两列不包含 XML 数据:  清单 3. 带投影和限制的简单 SELECT 语句select id, name, contactinfo
from clients
where status = 'Gold'
  正如您所预料,您可以基于这样的查询创建视图,下面的 "goldview" 可以说明这一点:  清单 4. 创建包含 XML 列的视图create view goldview as
select id, name, contactinfo
from clients
where status = 'Gold'
  不幸的是,很多事情光用 SQL 是无法解决的。通过纯 SQL 语句可以检索整个 XML 文档(刚才已证实这一点),但是却不能指定基于 XML 的查询谓词,也不能检索 XML 文档的某一部分或者 XML 文档中特定的元素值。换句话说,使用纯 SQL 不能对 XML 文档中的片段进行投影、限制、连接、聚集或排序操作。例如,您不能单独检索 Gold 客户的 email 地址或居住在邮政编码为 "95116" 的地区的客户的姓名。为了表达这些类型的查询,需要使用带 XML 扩展的 SQL(SQL/XML)、XQuery 或结合使用这两种查询语言。  下一节将探讨 SQL/XML 的几个基本特性。在接下来的文章中,我们将学习如何编写 XQuery 以及如何将 XQuery 与 SQL 结合使用。  SQL/XML 查询  顾名思义,SQL/XML 被设计用来为 SQL 和 XML 两者之间搭一座桥。它首先是 SQL 标准的一部分,经过演化现在包括将 XQuery 或 XPath 表达式嵌入 SQL 语句的规范。XPath 是用于导航 XML 文档以便发现元素或属性的一种语言。XQuery 包括对 XPath 的支持。 上一页12345678下一页   请务必注重,XQuery(和 XPath)表达式是大小写敏感的。例如,引用 XML 元素 "zip" 的 XQuery 并不适用于名为 "ZIP" 或 "Zip" 的 XML 元素。SQL 程序员有时候很难记住大小写敏感这一点,因为 SQL 查询语法答应使用 "zip"、"ZIP" 和 "Zip" 来引用同一个列名。  DB2 Viper 提供了超过 15 个 SQL/XML 函数,通过这些函数可以搜索 XML 文档中的特定数据,将传统数据转换成 XML,将 XML 数据转换成关系数据,以及执行其他有用的任务。本文不讨论 SQL/XML 的所有方面,而只是谈到几种常见的查询挑战,以及一些要害的 SQL/XML 函数如何解决这些挑战。  根据 XML 元素值 “限制 结果  SQL 程序员经常编写根据某种条件限制从 DBMS 返回的行的查询。例如,清单 3 中的 SQL 查询限制从 "clients" 表中检索的行,使之只包括那些具有 "Gold" 状态的客户。在这个例子中,客户的状态可在 SQL VARCHAR 列中捕捉。但是,假如您想根据某种应用于 XML 列中数据的条件对搜索进行限制,那么应该怎么做呢?SQL/XML 的 XMLExists 函数为完成该任务提供了一种手段。  通过 XMLExists 可以在 XML 文档中找到一个元素,并测试它是否满足某个特定的条件。假如用在 WHERE 子句中,则 XMLExists 可以限制返回的结果,使之只包括那些包含具有特定 XML 元素值的 XML 文档的行(换句话说,指定的值等于 "true")。  让我们看看早先碰到的一个查询问题。假如您想找到居住在具有特定邮政编码的地区的所有客户的姓名。您也许还记得,"clients" 表的一个 XML 列中存储了客户的地址(包括邮政编码)。(见 图 2。)通过使用 XMLExists,可以从 XML 列中搜索目标邮政编码,并相应地限制返回的结果集。下面的 SQL/XML 查询返回居住在邮政编码为 95116 的地区的客户的姓名: 上一页123456789下一页   清单 5. 根据 XML 元素值限制结果select name from clients
where xmlexists('$c/Client/Address[zip="95116"]'
passing clients.contactinfo as "c")
  第一行是一个 SQL 子句,指定仅检索 "clients" 表 "name" 列中的信息。WHERE 子句调用 XMLExists 函数,指定一个 XPath 表达式,这个表达式使 DB2 找到 "zip" 元素并检查元素值是否为 95116。"$c/Client/Address" 子句表明 DB2 用于定位 "zip" 元素的 XML 文档层次结构中的路径。通过使用可以从节点 "$c"(稍后将会解释)访问的数据,DB2 将从 "Client" 元素中找到它的子元素 "Address",以便检查邮政编码("zip" 值)。最后一行决定 "$c" 的值:它是 "clients" 表的 "contactinfo" 列。因此,DB2 检查 "contactinfo" 列中的 XML 数据,从根元素 "Client" 深入到 "Address" 子元素再找到 "zip" 子元素,然后判定客户是否居住在目标地区。假如客户住在目标地区,则 XMLExists 函数的返回值为 "true",DB2 将返回与那一行相关的客户的姓名。  在使用 XMLExists 查询谓词时经常会出现一个错误,如 清单 6 所示。  清单 6. 根据 XML 元素值限制结果时采用的不正确语法select name from clients
where xmlexists('$c/Client/Address/zip="95116" '
passing clients.contactinfo as "c")
  虽然这个查询也可以成功地执行,但是它不能限制结果,使之仅包含居住在邮政编码为 95116 的地区的客户。(这是由于标准中规定的语义造成的,而且这并不是 DB2 所特有的。)为了限制结果,使之仅包含居住在邮政编码为 95116 的地区的客户,需要使用前面 清单 5 中展示的语法。 上一页12345678910下一页   您可能很想知道如何在应用程序中包括限制 XML 数据的查询。虽然本文不具体讨论应用开发话题,但还是提供了一个 简单的 Java 例子,这个例子在一条 SQL/XML 语句中使用一个参数标记位将输出限制为居住在给定地区的客户的信息。  “投影 XML 元素值  现在让我们考虑一种稍微有些不同的情景,假设您想将 XML 值投影到返回的结果集。换句话说,我们要从 XML 文档中检索一个或多个元素值。有很多方法可以做这件事。首先我们使用 XMLQuery 函数来检索一个元素的值,然后使用 XMLTable 函数来检索多个元素的值,然后将这些映射到一个 SQL 结果集的列。  我们来考虑如何解决之前摆在我们面前的一个问题:如何创建一个列出 Gold 客户的 email 地址的报告。下面 清单 7 中的查询调用 XMLQuery 函数来完成这项任务:  清单 7. 检索符合条件的客户的 email 信息select xmlquery('$c/Client/email'
passing contactinfo as "c")
from clients
where status = 'Gold'
  第一行指定要返回根元素 "Client" 的 "email" 子元素的值。第二行和第三行表明 DB2 在哪里可以找到该信息 —— 在 "clients" 表的 "contactinfo" 列中。第四行进一步限制查询,表明您只对 Gold 客户的 email 地址感爱好。这个查询将返回一组 XML 元素和值。例如,假如有 500 名 Gold 客户,每个客户有一个 email 地址,那么输出将是一个单列的结果集,一共有 500 行,如 清单 8 所示:  清单 8. 之前查询的示例输出1
--------------------------------------------
<email>user5976@anyprovider.com</email>
. . .
<email>someID@yahoo.com</email>  
上一页234567891011下一页   假如每个 Gold 客户有多个 email 地址,那么需要指示 DB2 只返回首要的地址(也就是在客户的 "contactinfo" 文档中找到的第一个 email 地址)。为此,可以修改查询的第一行中的表达式:  清单 9. 检索每个符合条件的客户的第一个 email 地址select xmlquery('$c/Client/email[1]'
passing contactinfo as "c")
from clients
where status = 'Gold'
  最后,假如有些 Gold 客户没有 email 地址,那么可能要编写一个查询从结果集中排除 null 值。为此可以修改之前的查询,添加另一个谓词到 WHERE 中,以测试是否缺少 email 信息。您已经熟悉了一个可以帮您实现这一点的 SQL/XML 函数 —— 那就是 XMLExists。清单 10 展示了如何重新编写之前的查询,以便过滤掉那些联系方式(存储为 XML 文档)中缺少 email 地址的 Gold 客户的行:  清单 10. 对于至少有一个 email 地址的客户,检索每个符合条件的客户的第一个 email 地址select xmlquery('$c/Client/email[1]'
passing contactinfo as "c")
from clients
where status = 'Gold'
and xmlexists('$c/Client/email' passing contactinfo as "c")
  现在我们考虑一个稍微不同的情景,假设您要检索多个 XML 元素值。XMLTable 可以从 XML 列中的数据生成标量输出,可以为程序员提供 XML 数据的 “关系 视图,因此非常有用。与 XMLExists 和 XMLQuery 一样,XMLTable 函数使 DB2 在 XML 文档层次结构中定位到感爱好的数据。然而,XMLTable 还包括一些子句,用于将目标 XML 数据映射到 SQL 数据类型的结果集列。 上一页3456789101112下一页   考虑以下查询(清单 11),该查询投影存储在 "items" 表中的关系数据和 XML 数据。(关于 "items" 表请查看 图 1)。评论 ID、客户 ID 和评语存储在 "comments" 列中的 XML 文档中。商品名称存储在一个 SQL VARCHAR 列中。  清单 11. 检索多个 XML 元素并将每个元素转换成传统的 SQL 数据类型select t.Comment#, i.itemname, t.CustomerID, Message from items i,
xmltable('$c/Comments/Comment' passing i.comments as "c"
columns Comment# integer path 'CommentID',
  CustomerID integer path 'CustomerID',
  Message varchar(100) path 'Message') as t
  第一行指定将包含在结果集中的列。查询中后面的几行表明,用引号括起来、并且以变量 "t" 为前缀的列是基于 XML 元素值的列。第二行调用 XMLTable 函数指定包含目标数据("i.comments")的 DB2 XML 列和在该列的 XML 文档中的路径,通过该路径可以定位感爱好的元素(在根元素 "Comments" 的子元素 "Comment" 中)。第 3 到 5 行的 "columns" 子句标识出将被映射到第一行指定的 SQL 结果集中的输出列的特定 XML 元素。这个映射需要指定 XML 元素值将被转换成的数据类型。在这个例子中,所有 XML 数据被转换成传统的 SQL 数据类型。  图 4 展示了运行该查询得到的示例结果。可以看到,输出是一个简单的 SQL 结果集。注重,列名已经被变成大写形式 —— 这在 SQL 中是很常见的。  图 4. 使用 XMLTable 函数的查询的示例输出    假如需要的话,还可以使用 XMLTable 创建包含 XML 文档的结果集。例如,以下语句产生类似于上述结果的结果集,不同的是 "Message" 数据被包含在一个 XML 列中,而不是包含在一个 SQL VARCHAR 列中。 上一页45678910111213下一页   清单 12. 检索多个 XML 元素并将它们转换成传统的 SQL 或 XML 数据类型select t.Comment#, i.itemname, t.CustomerID, Message from items i,
xmltable('$c/Comments/Comment' passing i.comments as "c"
columns Comment# integer path 'CommentID',
  CustomerID integer path 'CustomerID',
  Message XML by ref path 'Message') as t
  创建 XML 数据的关系视图  正如您可能想像到的那样,SQL/XML 函数可用于定义视图。假如要为 SQL 应用程序的程序员提供本地 XML 数据的关系模型,那么这种功能非凡有用。  为 XML 列中的数据创建关系视图并不比投影 XML 元素值复杂多少。您只需编写一个 SQL/XML SELECT 语句,在语句中调用 XMLTable 函数,并以此作为视图定义的基础。下面 清单 13 中的例子基于 "items" 表的 XML 列和非 XML 列中的信息创建一个视图。(这类似于 清单 11 中的查询。)  清单 13. 基于 XMLTable 的输出创建视图create view commentview(itemID, itemname, commentID, message, mustrespond) as
select i.id, i.itemname, t.CommentID, t.Message, t.ResponseRequested from items i,
xmltable('$c/Comments/Comment' passing i.comments as "c"
columns CommentID integer path 'CommentID',
  Message varchar(100) path 'Message',
  ResponseRequested varchar(100) path 'ResponseRequested') as t;
  虽然在 XML 列中的数据上创建关系视图很轻易,但是用起来要小心。在对那样的视图发出查询时,DB2 不使用 XML 列索引。因此,假如以 ResponseRequested 列为索引,并发出一条将 "mustrespond" 列的结果限制为某个特定值的 SQL 查询,那么 DB2 将读取所有的 XML 文档,并搜索适当的 "ResponseRequested" 值。除非数据量不大,否则这样做会降低运行时性能。然而,假如在那些视图上运行的查询还包含有严格限制性的谓词,且参与索引的项中有传统的 SQL 类型的项(在这个例子中可以是 "i.id" 或 "i.itemname"),那么可以缓解潜在的运行时性能问题。DB2 使用关系索引将符合条件的行过滤到一个较小的量,然后在返回最终结果之前,将更多的 XML 查询谓词应用到这些临时的结果上。 上一页567891011121314下一页   连接 XML 数据和关系数据  现在,您可能想知道如何连接 XML 数据和非 XML 数据(例如基于传统 SQL 类型的关系数据)。DB2 使您可以仅用一条 SQL/XML 语句来做到这一点。有很多方法可用来制定那样的连接,这取决于数据库模式和工作负载需求,不过这里我们只谈论一个例子。您也许会感到惊奇,其实您已经知道足够多关于 SQL/XML 的东西,完全可以实现这种连接。  还记得吗,"items" 表中的 XML 列包含一个 "CustomerID" 元素。这可以作为与 "clients" 表中基于整数的列 "id" 的一个连接键。因此,假如要获得一个报告,其中列出对您的一件或多件产品发表了评论的客户的姓名和状态,那么需要将一个表中的 XML 元素值与来自另一个表中的 SQL 整数值相连接。实现这一点的一种方法是使用 XMLExists 函数,如 清单 14 所示:  清单 14. 连接 XML 数据和非 XML 数据select clients.name, clients.status from items, clients
where xmlexists('$c/Comments/Comment[CustomerID=$p]'
passing items.comments as "c", clients.id as "p")
  第一行标识出要包括在查询结果集中的 SQL 列以及查询中所引用的源表。第二行包括了连接子句。这里,XMLExists 决定在一个目标源中的 "CustomerID" 值是否等于来自另一个目标源的值。第三行指定这两个源:第一个目标源是 "items" 表中的 XML 列 "comments",第二个目标源是 "clients" 表中的整数列 "id"。因此,假如客户对任何产品发表了评论,并且 "clients" 表中存在关于该客户的信息,那么 XMLExists 表达式将等于 "true",报告中将包括该客户的姓名和状态。 上一页6789101112131415下一页