概览 sql 与 xml 的互操作性,了解如何着手处理存储在数据库中的 xml 文档……
xml 数据通常用于现在的生产系统,是数据库实现的主要部分。过去,这一事实给数据库开发人员带来了设计问题,使得他们必须存储、查询和更新诸如非结构化 lob 的 xml 数据,或者将数据“分解”到关系表中,然后再将其重新组合。该方法导致编程的复杂且低效,因为这些访问机制是不成熟的。
首次随 oracle9i 数据库第 2 版推出的 oracle xml db 特性提供了用于在数据库中存储、检索和操作 xml 数据的新功能。然而,oracle 10g 第 2 版 xml db 极大地扩展了该初始方法,其中 xml 数据就像数据库中的 blob 一样。
本文适用于不了解 oracle 中 xml 实现的 dba 或开发人员:其中简要概述了 sql 和 xml 互操作性和如何着手处理存储在 oracle 10g 第 2 版数据库中的 xml 文档,此外,还介绍了 dba 和开发人员在数据库中使用 xml 之前应该熟悉的由 oracle 数据库 10g 第 2 版提供的大量新特性。使用这些 sql/xml 特性,可以轻松增强您在使用关系数据时获取的技能,以便帮助您使用 xml。
什么是 oracle 的 xml 实现?
oracle 已经通过数据库的 xml db 组件实现了 xml,该组件是企业版和标准版的标准特性。通过数据库配置助手 (dbca) 或者运行一个目录脚本 catqm.sql,可以轻松安装 oracle xml db。xml db 产品提供本文讨论的所有特性。
要验证是否安装了 xml db,只需运行下面的 sql:
select comp_name from dba_registry where comp_name like '%xml%'; comp_name oracle xml database |
也可以查看 xml db 模式及其对象来确认是否安装了 oracle xml db。
sql 和 xml 协作
sql/xml 标准已处在开发之中,以便提供一个允许我们从关系查询生成 xml 的机制,并反之使我们能够从 xml 文档提交 sql 数据。oracle 数据库 10g 第 2 版中的 oracle xml db 实现 sql 2003 和来自即将推出的 sql/xml 标准版的特性。oracle 中这些标准的实现使我们能够以在之前 sql 标准中不可能的方式查看 xml 文档。
新数据类型。oracle9i 数据库第 1 版中引入了一个新数据类型 xmltype,它允许在 sql 中访问数据库中的 xml 文档,同时允许 xml 开发人员在文档中使用 xml 标准。该数据类型通知数据库该内容是 xml 格式的,并允许我们在 xml 文档上执行查询。使用 xmltype(而非一个关系或 clob 实现)在应用程序和存储模型间提供一个分离层。该分离允许数据移到一个不同的存储模型,而不局限于 clob 或关系模型。xmltype 可用于创建表、列或视图。它还可用作参数和变量的数据类型。
内置的 xml 方法可以处理文档内容,允许我们创建、提取和索引 xml 数据。索引可以使用 b 树、文本索引和基于函数的索引执行。实际上,与 xpath 合并的 xmltype 数据可用于查看文档。该功能通过 pl/sql 和 java api 提供。xmltype 可用在 pl/sql、使用 jdbc 的 java 以及 oracle data provider for .net 中。这个功能强大且相对较新的数据类型将在本文中广泛使用。
存储结构。oracle 的 xml 实现为我们提供了以两种不同方式存储数据的灵活性:结构化存储和非结构化存储。xmltype 数据在存储为单个 lob 列时是非结构化的,在实现为对象集时是结构化的。
它的一个特定示例是 xmltype 表,该表可以使用这两种存储模型之一实现。xmltype 表在使用如下所示的“xmltype store as clob”语法实现为 lob 时是非结构化的:
create table table_name of xmltype xmltype store as clob; |
让我们重点看一下 xml 数据的结构化存储和非结构化存储并进行更详细的了解。
结构化存储。将结构化 xml 存储实现为一组对象。这些对象可通过具有可以在表间实现的引用约束的表以关系格式实现。它们也可以使用一个 xmlschema 实现,以便将一个 xmltype 文档分解为一组对象。
在使用关系表的情况中,可以因此对这些表进行预先设计,或者使用现有表。该文档实际上通过关系表存储为“虚拟”文档,并保留其逻辑结构。该方法可维护文档对象模型 (dom) 保真度,但是它不是文档的逐字节物理表示。这可以通过创建一个关于现有关系数据的 xmltype 视图来完成。
结构化存储比非结构化存储具有一些性能优势,选择它可通过表和索引设计提供更多的查询和更新优化。结构化存储上的 xml 操作有助于减少内存和存储,因为没有存储 xml 标记,而且存在更细粒度的数据检索和使用。基于 b 树和基于函数的索引使用得到了增强,而且使用 xpath 重写可在文档的部分上执行适当更新。所有这些都对性能有所帮助,本文稍后将详细讨论。
使用结构化存储也有一些缺点。插入和检索整个文档需要更多开销。此外,灵活性有限,因为只能存储匹配 xmlschema 的文档 ― 但是在某些情况中,这实际上是个优势。文档不存储为逐字节原始表示,而且不保留文档中的数据顺序。但是,这将不会出现数据丢失的情况。
我们通常会将高度结构化的数据看作是 xml,其中数据的每个元素都可以清晰定义。
非结构化存储。xml 可以使用 clob 存储以非结构化方式存储在 oracle 中,以便 sql 查询不会知道该数据的结构。该数据可以使用 oracle 的 xmltype 数据类型存储。
如果想让存储数据匹配文档的准确物理表示,可以选择非结构化数据。此外,如果该数据不经常更新,或者整个文档(而非部分文档)有大量的插入和读取,则将数据整体放在一处可能更高效。在某些情况中,您需要保持文档的灵活性,而且在这些情况中,xmltype 表或列可能是有用的。
非结构化存储的缺点是,对部分文档的更新通常不如结构化数据那样高效。此外,使用函数从 clob 构造 dom 的 xpath 操作将使用大量的系统资源。非结构化数据的其他问题是,sql 约束无法实现,而且内存管理也不是如此有效。
我们通常不将非结构化数据作为 xml 数据考虑,并将其中大部分或所有数据只看作是单个 clob。
了解了这些存储模型后,现在让我们来看一些命名特性。
命名空间中有什么?
这是一个大主题,也是需要了解的较复杂的 xml db 概念之一。在本文中,我们将介绍一些命名空间概念,以便您了解其要旨所在及其用法。
命名空间用于描述 xml 文档中的一组相关属性或元素。命名空间可用于确保文档构造有完全唯一的名称。xmlschema 尤其要利用该特性,因为目标命名空间通常与 xmlschema 的 uri 相同。命名空间示例如下所示。请注意,该命名看起来类似于一个 internet url。该命名标准由万维网联盟 (w3c) 建议,但并非必需。该 uri 用于唯一标识数据库中注册的 xmlschema 的名称,而且无需是文档所处位置的物理 url。
xmltype 方法和 xml 函数使用命名空间前缀。当 xml 文档没有目标命名空间时,该命名空间前缀将位于 nonamespace 命名空间中。属性 xsi:nonamespaceschemalocation 可用于模式 uri。
如果元素不具备另一个命名空间前缀,默认情况下,在将某个命名空间应用到描述它的元素时,可使用该命名空间。
通常您将看到两个特定的命名空间。它们是 http://www.w3.org/2001/xmlschema(完整的 xmlschema 命名空间)和 http://xmlns.oracle.com/xdb(oracle 提供的 xml db 命名空间)。第二个命名空间具有写入基础 sql 函数的函数。一些 xpath 函数也进行重写。例如 ora:contains、ceiling、floor、not、string-length、substring 和 translate。xml db 使用的属性也属于该命名空间。
定义元素时,我们可以指定该元素的命名空间。在 xml 中,我们可以将它定义为
<elementname xmlns:ab="http.name.com" /> |
如您所见,我们提供了一个 elementname,它是我们将使用的元素(例如,“orders”)。xmlns 是保留字,它告诉我们这是一个命名空间定义。“ab”部分称为前缀。该前缀是一个绑定到 url 的简称。在本例中,ab 绑定到‘http.name.com’,而且 ab 可以加为元素名的前缀。
下面我们将创建一个非常小的模式,并声明绑定前缀 xs 到 http://www.w3.org/2001/xmlschema 以及声明前缀 xdb 到 http://xmlns.oracle.com/xdb。为了声明该命名空间,我们使用 xmlns 绑定它们。请注意,xs 前缀在本例中用在模式元素名称上。
<xs:schema xmlns:xs="http://www.w3.org/2001/xmlschema" xmlns:xdb= "http://xmlns.oracle.com/xdb" version="1.0"> <xs:element name="invoiceschema" xdb:defaulttable="invoiceschema"> <xs:complextype> <xs:sequence> <xs:element name="mailaddressto"> <xs:complextype> <xs:sequence> <xs:element name="person" type="xs:string" <xs:element name="street" type="xs:string"/> </xs:sequence> <xs:attribute name="id" type="xs:string" use="required"/> </xs:complextype> </xs:element> </xs:sequence> </xs:complextype> </xs:element> </xs:schema> |
这里我们只是粗略介绍了一下命名空间。
使用 xml
将数据添加到 xmltype 表和列中。正如您可能预期到的,有很多选项可供我们将 xml 数据添加到数据库中。可以使用 sql、pl/sql、java 和 c 程序插入数据。也可以使用 xmltype 表的 sql*loader 和 import/export 函数将数据加载到数据库中。可以使用 oracle streams 来移动 xml 数据,还可以使用 dbms_xdb 函数。现在,就让我们来探究一个简单易用的方式来加载 xml 数据。
在本例中,我们将使用 sql 插入将一个 xml 文档从文件加载到表中,以便从 bfile 加载该 xml 文档。该文件需要通过一个数据库目录访问。为此,我们将创建一个目录以指向存储该 xml 文档文件的文件。请注意,要执行本文中的示例,您必须确保所用用户具有 xdbadmin 角色。
create or replace directory xmldir as '/u01/app/oracle/admin/db01/sql'; |
现在我们可以用两种方式创建包含 xmltype 数据的表。第一个将是具有 xmltype 列的表。第二个将是定义为类型 xmltype 的表。
1. 创建一个包含 xml 列的表。
create table invoicexml_col ( inv_id number primary key, inv_doc xmltype); |
2. 创建一个 xml 表。
create table invoicexml_tbl of xmltype; |
以上每一个语句都在每个表中隐式创建两个索引 ― 一个用于主键,另一个用于 lob。
现在我们要将一个发票文档插入到这两个表中。请注意,我们要使用字符集 al32utf8,并指定它传送字符集编码来供要读取的文件使用。数据将插入这两个表以显示这两个 sql 语句间的相似性。
insert into invoicexml_col values (1, xmltype(bfilename('xmldir', 'invoicexml.txt'), nls_charset_id('al32utf8') )); insert into invoicexml_tbl values (xmltype(bfilename('xmldir', 'invoicexml.txt'), nls_charset_id('al32utf8'))); |
我们现在已经使用 xmltype 表和列将两个 xml 文档加载到了数据库中。就是这么简单。
注册和实现模式类型化数据
如果处理命名空间和存储技术不足,xmltype 数据也可以实现为模式类型化或非类型化数据。xmlschema 可将文档与提供文档结构和内容信息的 xmlschema 相关联。这为文档提供了更好的记录、验证和控制。该模式中的信息可用于提交更有效的查询和更新处理。xmlschema 也允许将 xml 文档存储为结构化存储数据,因为该文档可以通过 xmlschema 分解为一组对象。用于存储该文档的对象模型由模式定义确定。xmltype 方法 schemavalidate() 和 isschemavalid() 允许使用模式定义编辑 xmltype 文档。
我们如何创建模式并在 xml 文档中使用它?为此,我们需要创建一个 xmlschema 定义
- 注册该模式
- 创建一个基于 xmlschema 的表
- 将数据插入其中
下面的示例分四步进行了说明。
1. 首先,在目录 xml_dir 下的文件 invoiceformtest.xsd 中创建一个模式定义。该清单如下所示:
<xs:schema xmlns:xs="http://www.w3.org/2001/xmlschema" xmlns:xdb=http://xmlns.oracle.com/xdb version="1.0"> <xs:element name="invoiceformtest" xdb:defaulttable="invoiceformtest"> <xs:complextype> <xs:sequence> <xs:element name="mailaddressto"> <xs:complextype> <xs:sequence> <xs:element name="person" type="xs:string"/> <xs:element name="street" type="xs:string"/> <xs:element name="city" type="xs:string"/> <xs:element name="state" type="xs:string"/> <xs:element name="zipcode" type="xs:string"/> </xs:sequence> <xs:attribute name="id" type="xs:string" use="required"/> </xs:element> </xs:sequence> </xs:complextype> </xs:element> </xs:schema> |
2. 使用过程 dbms_xmlschema.registerschema 在数据库中注册上面的模式。您用来连接到数据库的用户名需要修改会话权限,以便该模式可以成功注册。
begin dbms_xmlschema.registerschema( schemaurl => 'http://xmlns.oracle.com/xdb/invoiceformtest.xsd', schemadoc => bfilename('xmldir','invoiceformtest.xsd'), csid => nls_charset_id('al32utf8')); end; / |
要删除该模式,只需运行 dbms_xmlschema.deleteschema 语句,如下所示:
begin dbms_xmlschema.deleteschema( schemaurl => 'http://xmlns.oracle.com/xdb/invoiceformtest.xsd', delete_option => dbms_xmlschema.delete_cascade_force); end; / |
3. 由于上面模式定义中的 xdb:defaulttable="invoiceformtest" 语句,以上语句还创建了表 invoiceformtest。没有它,将创建一个生成的名称(您不想使用该名称)。
4. 现在,我们准备将文档输入到 xml 文档内基于 xmlschema 的表中,该文档存储在 xml_dir 中并使用实现的模式指定的格式。
insert into invoiceformtest values (xmltype(bfilename('xmldir', 'invoiceformtest.txt'), nls_charset_id(' al32utf8'))); |
来看一下我们刚刚使用 xml 文档上的 object_value pseudocolumn 创建的内容。
sql> select object_value from invoiceformtest; object_value |
完成!
了解 xquery 函数和表达式
xquery 是 w3c 针对查询语言开发的一个标准,用于从 xml 文件提取信息。它允许我们访问物理 xml 文档或者通过 xml 视图虚拟化为 xml 文档的关系数据。使用的表达式可以是简单的查询或较大查询的一部分,而且可以包括标准函数,例如,日期/时间、算术或字符串函数以及用户定义的函数。xquery 是 oracle 数据库 10g 第 2 版中的新特性,并受 xmlquery() 和 xmltable() 函数支持。使用这两个函数的示例稍后在“xmlquery() 和 xmltable()”部分中有述。
xquery 使用 xpath 表达式(如下所述)定位 xml 文档中的详细项。可将其看作是 oracle 数据库 10g 第 2 版中引入的 xmlquery() 和 xmltable() 函数的基础,本文稍后有述。
以下将介绍一些更重要的 xquery 表达式(包括 xpath、xmlsequence 和 flwor)。
xpath、xmlsequence 和 flwor 表达式
xpath。xpath 提供使用定址技术在 xml 文档中定位项的方式,而且它通过一个文档结构使用逻辑路径处理这些项。它允许程序员在较高的抽象级别处理文档,方法是通过文档指定路由而非指向特定元素。xpath 使用节点概念定义路径在何处开始,然后定义一个“逻辑树”,该树包括诸如 attribute、self、parent、child 和 ancestor 等关系。换言之,xpath 会将 xml 文档建模为节点树。节点类型分为若干种,例如,元素节点、属性节点和文本节点。xpath 可以确定一个方式来为每个节点计算字符串值。
凭借 xpath 表达式,您可使用基于标准的方式查询和更新 xml 文档。我们将使用 extract、extractvalue、existsnode 和 xmlsequence 函数来演示 xpath 功能,期间将用到我们已创建的 invoicexml_tbl 文档以及插入到其中的数据。
查询要在 xpath 示例中使用的文档。让我们看一下如何使用 xpath 函数通过 sql 搜索 xml (xmltype) 数据。为此,来看一下要使用 object_value pseudocolumn 从 xmltype 表检索 xml 文档时涉及的文档。
sql> select object_value from invoicexml_tbl; object_value |
使用 extract。使用 extract 时,通过将 extract 与 object_value 合并,可从文档选择单个节点及其叶节点。换言之,我们可以查看存储为 xmltype 的 xml 文档。无论我们使用的是结构化数据还是非结构化数据以及数据是否基于模式,情况都是如此。开始提取 mailaddressto 节点及其叶节点。
select extract(object_value, '/invoice/mailaddressto') from invoicexml_tbl; extract(object_value,'/invoice/mailaddressto') |
如您所见,该输出包括文档的 mailaddressto 部分,不适合打印(格式化)。此外,用于实现它的语法非常简单。重要的是,我们能够在无需转储整个内容的情况下查看文档。
使用 extractvalue。叶节点中存在的数据值可以使用 extractvalue 提取。较高级的节点(例如 mailaddressto)无法使用该函数提取。请注意,它的输出不是 xml 语法格式,其中只包含该数据值。
select extractvalue(object_value, '/invoice/mailaddressto/person') person from invoicexml_tbl; person joe smith |
使用 existsnode。existsnode 以类似的方式使用,以便在节点级(只能在节点级)搜索文档的特定值。它返回一个 true 或 false 标志来指定搜索是否成功。= 1 谓词不是次数,表示的是 true 条件,而 = 0 表示 false。
select count(*) from invoicexml_tbl where existsnode( object_value, '/invoice/mailaddressto[person="joe smith"]') = 1; count(*) 1 |
使用 xmlsequence。与 extractvalue(只可从单个节点提取的值)不同,xmlsequence 可用于查看文档的多个节点或某个片段。它通过创建包含 xmltype 对象的虚拟表来完成此操作。让我们使用 mailaddressto 分支节点来对 extractvalue 和 xmlsequence 进行比较。
select extractvalue(object_value, '/invoice/mailaddressto') from invoicexml_tbl; from invoicexml_tbl * error at line 2: ora-19025: extractvalue returns value of only one node |
该 ora-19025 消息具有自我说明性。幸运的是,我们可以通过重构该查询并使用 xmlsequence 解决该问题,如下所示:
select value(addr) value(addr) |
由于 xmlsequence 创建一个虚拟表,我们也可以在叶节点上使用该函数。
select value(person) value(person) |
flwor。for、let、where、order by 和 return(flwor;发音为“flower”)是 xquery 语法中最重要且最强大的表达式之一。for 或 let 必须存在,where 和 order by 是可选的,而 return 是必需的。flwor 本身就是一个有待介绍的大主题。本节将为您简要介绍一下该语句的强大功能。
for 通过迭代方式以变量的列出顺序绑定一个或多个变量。前面列出的值之后可用在一组后续的值中。这种工作方式也类似于 sql from 子句。前面列出的值之后可用在一组后续的值中,如下所示:
for $var in (1,2,3) , $varplus5 in (5+$var, 5+$var, 5+$var) |
这三个迭代将 $var 和 $varplus5 分别设置为 1,6;2,7 和 3,8。
let(类似于 for)以迭代方式绑定变量,可以使用以前计算的值来计算值。与使用 for 一样,可将 let 看作 sql from 字句。let 也可以用于执行连接。
where 过滤数据的方式与 sql where 子句相同。
order by 可以随意对数据进行排序。
return 从过滤和排序后的 flwor 表达式返回最终结果集。
flwor 与 xmlquery() 协作。以下是查询和连接两个文档的示例:用 party 键将 partys.xml 连接到 orders.xml 文档。该 xml 数据位于 oracle xml db 信息库中。为此,我们将使用 xmlquery();flwor;以及 xquery 函数 doc、count、avg 和 integer。它们位于内置 xquery 函数的命名空间中,http://www.w3.org/2003/11/xpath_functions。
以下查询读取结果如下所示:使用函数 fn:doc,对于 (for) partys.xml 中的所有 partyno 属性,连接 (let) orders.xml 中匹配 partyno 的所有定单元素(变量 $p 绑定在 for 语句中)。这将生成一个项流($p 和 $o),其中 $p 表示一方的编号,而 $o 表示该方的一组定单。获取其中 (where) 有多个定单的项。使用命名空间 fn 中的 xquery 函数 avg 对平均数 amt 进行降序排序 (order by)。 amt 附加到定单元素 $o。返回该方的编号(绑定到 $p)和子元素定单计数。
select xmlquery()('for $p in fn:doc("/public/partys.xml")/partys/party/@partyno
|
是的,该查询正在进行大量工作并在 xml 文档上显示使用 flwor 表达式时可能出现的结果。
xquery ora:函数
oracle xml db 提供五个开发人员可以使用的 xquery 函数。它们在 http://xmlns.oracle.com/xdb 命名空间中实现,该命名空间使用前缀 ora:。除此之外还有另外两个 xpath 函数。它们仍然不是 sql/xml 标准的一部分,但预期将来会是。该 ora:view 函数是一个特别有价值的函数,可用于将关系数据转换为 xml 格式。下面将对所有这些函数进行介绍。
ora:view xquery 函数。这是在关系表上创建视图的有用函数。这可以使它们看起来像 xml 文档。
syntax: ora:view ([db-schema string, ] db_table string) returns document-node (element()) |
本文稍后将给出使用 ora:view 的示例。
ora:contains xquery 函数。命名空间前缀“ora:”和名称“contains”一起使用可创建 xpath 函数,该函数可用作 sql/xml existsnode、extract 和 extractvalue 函数的 xpath 参数。这使您能够使用文本谓词执行结构搜索。
如果 text_query 与 input_text 匹配,该函数将返回一个正数。如果它们不匹配,则返回 0。当它们与 existsnode、extract 或 extractvalue 一起使用时,您需要包括命名空间映射参数 xmlns:ora="http://xmlns.oracle.com/xdb"。ora:contains 的语法如下所示。当未定义 policy_owner 时,所有者是当前用户。policy_name 是要应用的匹配规则的名称。默认的匹配规则由 ctxsys.default_policy_oracontains 定义。
syntax: ora:contains (input_text, text_query [, policy_name] [, policy_owner]) |
下面的示例显示了如何使用 ora:contains 作为 existsnode 的参数。
select count(*) from invoicexml_tbl where existsnode(object_value, '/invoice/mailaddressto/person [ora:contains(text(), "joe smith") > 0]', 'xmlns:ora="http://xmlns.oracle.com/xdb"') = 1; count(*) 1 |
ora:matches xquery 函数。该函数使用正则表达式匹配文本。这类似于 sql regexp_like 条件,但是它使用 xquery 参数而非 sql 数据类型。如果 target_string 匹配正则表达式 match_pattern,则返回 true;否则,返回 false。正如您在下面语法中所见,可以添加 match_parameter 以指定在搜索中使用的附加标准。以下示例是提供区分大小写。
syntax: ora:matches (target_string, match_pattern [, match_parameter]) |
ora:replace xquery 函数。该函数在满足 match_pattern 的情况下 (true) 使用 replace_string 替换 target_string,这是对 ora:matches 函数的扩展。与使用 ora:matches 一样,使用正则表达式来匹配文本。
syntax: ora:replace (target_string, match_pattern, replace_string [, match_parameter]) |
ora:sqrt xquery 函数。顾名思义,该函数返回提供数字的平方根值。
syntax: ora:sqrt (number) |
xpath 扩展函数:ora:instanceof 和 ora:instanceof-only。当知道属性和元素的数据类型时,oracle xml db 可以支持基于模式的数据。由于 xpath 1.0 不知道数据类型信息,因此在命名空间 http://xmlns.oracle.com/xdb 中有 xml db 扩展函数,它们使您能够将 xml 文档节点限制为特定数据类型。函数 ora:instanceof 可用于将节点限制为数据类型或子类型,而 ora:instanceof-only 只将节点限制为数据类型。子类型是用于扩展或限制类型的特性。
syntax: ora:instanceof(nodeset-expr, typename [, schema-url]) syntax: ora:instanceof-only(nodeset-expr, typename [, schema-url]) |
在上面的语法中,nodeset-expr 通常是一个相关的 xpath 表达式。如果任何节点的数据类型与类型名匹配,则该函数返回 true,否则返回 false。类型名可使用命名空间前缀限定。
对于非基于模式的数据,这些函数将返回 false,因为这些数据没有与元素和属性相关的数据类型。
使用 fn:doc 和 fn:collection 函数在 xml db 信息库中查询 xml 数据
有两个重要的 xquery 函数可用于在 xml db 信息库中查询所有资源。fn:doc 是一个 xquery 函数,可以获取包含 xml 数据的信息库文件。该文件资源由其 url 参数指出。xquery 变量可以使用 flwor 表达式 for 和 let 绑定到数据。xquery 函数 fn:doc 可用于读取存储在 xml db 信息库中的单个 xml 文档。
在信息库中查询资源的第二个 xquery 函数是 fn:collection。该函数可以返回存储在信息库中同一文件夹中的大量类似文档。
现在,我们将通过创建信息库资源来演示 fn:doc 和 fn:collection 如何处理一些简单示例。首先,使用 dbms_xdb pl/sql 程序包创建一个资源。这可用于在 xml db 中管理资源。该 createresource 过程在本例中用于创建包含 orders 和 partys xml 文档的新文件资源。
创建用于以下示例的定单和定方资源名。其中有一个名为 ordersnamespace.xml 的附加资源,它是一个使用命名空间的示例。
declare res boolean; ordersxmlstring varchar2(500):= '<?xml version="1.0"?> <orders> <order orderno="15" partyno="1111" itemname="widget" amt="5000"/> <order orderno="25" partyno="1111" itemname="do dad" amt="2000"/> <order orderno="35" partyno="2222" itemname="all purpose item" amt="7000"/> <order orderno="45" partyno="3333" itemname="the best thing" amt="15000"/> </orders>'; partysxmlstring varchar2(500):= '<?xml version="1.0"?> <partys> <party partyno="1111" partyname="abc corp" partycity="toronto"/> <party partyno="2222" partyname="freds inc" partycity="chicago"/> <party partyno="3333" partyname="gofaster corp" partycity="montreal"/> </partys>'; ordersxmlnsstring varchar2(500):= '<?xml version="1.0"?> <orders xmlns="http://order.com"> <order orderno="15" partyno="1111" itemname="widget" amt="5000"/> <order orderno="25" partyno="1111" itemname="do dad" amt="2000"/> <order orderno="35" partyno="2222" itemname="all purpose item" amt="7000"/> <order orderno="45" partyno="3333" itemname="the best thing" amt="15000"/> </orders>'; begin res := dbms_xdb.createresource('/public/orders.xml', ordersxmlstring); res := dbms_xdb.createresource('/public/partys.xml', partysxmlstring); res := dbms_xdb.createresource('/public/ordersnamespace.xml', ordersxmlnsstring); end; |
我们可以看到刚刚通过查询 resource_view 创建的资源,如下所示:
sql> select any_path, res from resource_view where any_path like '%partys%'; any_path res /public/partys.xml <resource xmlns="http://xmlns.oracle.com/xdb/xdbresource.xsd"> <creationdate>2006-06-19t17:12:00.414000</creationdate> <modificationdate>2006-06-19t17:12:00.414000</modificationdate> <displayname>partys.xml</displayname> <language>en-us</language> <characterset>windows-1252</characterset> <contenttype>text/xml</contenttype> <refcount>1</refcount> </resource> |
接下来,使用 fn:doc 显示单个 partys.xml 文档。
select xmlquery('for $p in fn:doc("/public/partys.xml") partys |
对 fn:collection 的使用与 fn:doc 稍有不同,它可返回存储在信息库中同一文件夹中的所有文档。从该语句生成的输出不适合打印,手动进行以下格式化以使该文档可读性更高。
select xmlquery('for $p in fn:collection("/public") collection_public <orders xmlns="http://order.com"> <partys> |
还有其他大量 dbms_xdb 过程和函数可用于管理所有 xml db 资源,例如,提供删除资源和创建文件夹的功能。
xmlquery() 和 xmltable()
oracle 数据库 10g 第 2 版中引入了 xmlquery() 和 xmltable() 函数。它们在 sql 和 xml 之间提供了强大的接口,允许我们查询、构造和转换关系数据,就像它是 xml,反之也可以将 xml 数据当作关系数据来处理。通常,我们使用 xmlquery() 从关系数据生成 xml 文档,并使用 xmltable() 从 xml 数据创建关系视图。它们仍然不是 ansisql 的一部分,但预计会包括在将来的标准中。
xmlquery() 函数。该函数用于构造 xml 数据并使用 xquery 来查询该数据。它支持在 sql 上下文中执行 xquery 表达式。我们也可以通过使用 ora:view 为关系数据创建动态 xml 视图来查询关系数据,并能够操作部分 xml 文档而非整个文档。
xmlquery() 将 xquery 表达式作为字符串文字,将可选的 xquery“上下文项”作为 sql 表达式。该表达式设置将在其中评估 xquery 表达式的 xpath 上下文。该函数将 xquery 表达式结果作为 xmltype 实例返回。xmlquery() 函数也可以将 sql 表达式作为参数,其中当评估表达式时,值绑定到 xquery 变量。这些结果作为 xmltype 实例返回。
来看两个示例,其中我们针对关系数据和 xml 数据使用了 xmlquery()。
• 将 xmlquery() 与 ora:view 和 flwor 表达式合并的示例。在第一个示例中,我们将在物理关系表上使用 xmlquery()。xmlquery() 必须操作 xml 数据,这可通过使用 hr 模式中关系表 employee 和 department 上的 ora:view 特性完成。当 ora:view 用在这两个关系表上之后,它们将显示为 xml,然后我们就能够使用 xquery 表达式,包括嵌套的 flwor 表达式。
下面的查询正在进行以下操作:对于每个部门,获取部门 id,对于匹配部门 id 且佣金高于 30% 的雇员,返回雇员的名和姓。请注意,for 表达式在该查询中使用了两次。
select xmlquery( high_commission_emp_names |
正如您所见,结果不适合打印。该查询语法有点复杂,但它显示出我们能够使用 ora:view 将关系数据转换为 xml,并能够针对 xml 文档执行连接操作以及将谓词应用到文档中的数据上。
• 将 xmlquery() 与 xmltype 列和 flwor 表达式一起使用的示例。表 invoicexml_col(我们在上面创建的,并在其中插入了一个文档)包含一个 xmltype 列 inv_doc。存储在该列中的 invoice 数据是 xml 格式的。在该查询中,我们使用具有 passing 子句的 xmlquery() 函数将 xmltype 列 inv_doc 传递到 xquery。请注意我们是如何从基于 where 谓词的 invoice 文档返回一些特定字段的。实际上,我们能够进入 xml 文档并根据谓词检索特定字段,而非只能够将此文档作为 clob 查看。
下面的 select 语句适用于 invoicexml_col 的所有行。然后我们可以使用 for 语句迭代所有发票行。where 谓词取出 zipcode 12345 的元素,然后返回 city、state 和 zipcode,以及是否为 zipcode 输入了正确的 city 和 state。 if…then…else 构造将体现该语法的一些附加功能。
select xmlquery( ny_invoice |
这两个查询阐释了我们必须以详细的分段方式查看和处理 xml 文档的功能。提供的功能类似于我们用关系数据进行的操作 ― 令人惊奇!
xmltable() 函数。该函数支持将 xml 值解释为表或集。它用于从 xquery 表达式的评估返回表和列,而不是像通常那样返回一个序列作为 xquery。可以查询 xmltype 数据并将 xml 结果分割或分解为关系格式 ― 可将其看作是创建一个虚拟表。然后,可以使用该虚拟表将数据插入到其他表中,或者查询该虚拟表。关系视图也可以针对 xml 数据进行构造。xmltable() 函数也可用在 sql from 子句中。
• 将 xmltable() 与 columns 子句一起使用的示例。我们将使用前面介绍过 invoicexml_col 表来阐释如何使用 xmltable() 将 xml 数据转换为关系格式。在下面的示例中,xmltable() 访问存储在列 inv_doc 中的 invoice 文档。使用 columns 子句将所需数据元素的路径映射到新的名称和格式。xmltable() 函数返回数据作为虚拟表,该查询的结果与我们已经查询过的关系表一样。请注意,通过在该查询底部使用的 where 子句,我们可以过滤 xml 数据,过滤的方式与使用任何为关系数据编写的 sql 查询的方式完全相同。
该查询和输出如下所示:
select inv_id, a.personname, a.streetname, a.cityname, a.state, a.zipcode from invoicexml_col, xmltable('/invoice' passing invoicexml_col.inv_doc columns personname varchar2(10) path '/invoice/mailaddressto/person', streetname varchar2(20) path '/invoice/mailaddressto/street', cityname varchar2(10) path '/invoice/mailaddressto/city', state varchar2(5) path '/invoice/mailaddressto/state', zipcode varchar2(7) path '/invoice/mailaddressto/zipcode' ) a where a.cityname like 'new%'; inv_id personname streetname cityname state zipcode 1 joe smith 10 apple tree lane new york ny 12345 |
执行 xml
如文中所述,我们已经可以执行查看 xml 文档的查询。下一步是考虑使这些查询快速执行的方式。可以解释 xml 查询并创建索引来帮助提高 xml 数据访问的性能。特别地,我们能够以类似过去调整 sql 的方式改善 xpath 函数的性能。此外,对于 sql 调整,在某些情况中,重构 xml 查询也有助于更改和改进访问路径。
基于函数的索引可用在结构化和非结构化的 xmltype 表上,无论它们是否基于模式。我们还可能想对诸如 existsnode 的函数利用二进制索引,即根据谓词评估为 true