网站首页/技术开发列表/内容

Oracle XQuery查询、构建与转换XML(1)

技术开发2024-04-12阅读
 

在 Oracle 数据库 10g 第 2 版中,Oracle 引入了一个与该数据库集成的全功能自带 XQuery 引擎,该引擎可用于完成与开发支持 XML 的应用程序相关的各种任务。XQuery 是一种用于处理 XML 数据模型的查询语言,它实际上可操作任何类型的可用 XML 表达的数据。尽管 Oracle XQuery 实施使您可以使用数据库数据和外部数据源,但在处理数据库中存储的结构化数据方面,Oracle XML DB 通常可以显著提高性能。

本文提供的示例不仅演示了在什么场合下以及如何使用 XQuery 查询、构建和转换 XML,而且还演示了如何监控和分析 XQuery 表达式的性能执行,从而找到更高效的方法来处理同一工作负载。

基于关系数据构建 XML

在需要的情况下(例如,向 Web 服务发送结果),您可能要基于关系数据构建 XML。要在 Oracle 数据库 10g 第 2 版之前的版本中完成此任务,通常需要使用 SQL/XML 生成函数,如 XMLElement、XMLForest 和 XMLAgg()。在 Oracle 数据库 10 g 第 2 版中,XQuery 将比这些函数更为高效。具体而言,在 XQuery 表达式内部使用 ora:view XQuery 函数,您可以查询现有的关系表或视图以及即时构建 XML,从而不必通过关系数据显式创建 XML 视图。列表 1 中的 PL/SQL 代码演示了如何使用 ora:view 基于示例数据库模式 HR 的默认员工关系表中存储的数据构建 XML 文档。

列表 1:使用 ora:view 基于关系数据创建 XML

BEGIN

IF(DBMS_XDB.CREATEFOLDER('/public/employees')) THEN

DBMS_OUTPUT.PUT_LINE('Folder is created');

ELSE

DBMS_OUTPUT.PUT_LINE('Cannot create folder');

END IF;

COMMIT;

END;

/
DECLARE

XMLdoc XMLType;

BEGIN

SELECT XMLQuery(

'for $j in 1

return (

{

for $i in ora:view("HR", "employees")/ROW

where $i/EMPLOYEE_ID <= 102

return (

{xs:string($i/EMPLOYEE_ID)}

{xs:string($i/LAST_NAME)}

{xs:integer($i/SALARY)}

)} )'

RETURNING CONTENT) INTO XMLdoc FROM DUAL;

IF(DBMS_XDB.CREATERESOURCE('/public/employees/employees.xml', XMLdoc)) THEN

DBMS_OUTPUT.PUT_LINE('Resource is created');

ELSE

DBMS_OUTPUT.PUT_LINE('Cannot create resource');

END IF;

COMMIT;

END;

/

在列表 1 中的第一个 PL/SQL 过程中,您只是在 XML 信息库中创建了一个新文件夹。在该信息库文件夹中,您随后将存储此处显示的第二个 PL/SQL 过程中创建的 XML 文档。第二个 PL/SQL 过程首先发出 SELECT 语句,该语句使用 XMLQuery SQL 函数基于关系数据构建 XML。对于 XQuery 表达式(XMLQuery 在此处将其用作参数)而言,请注意嵌套的 FLWOR 表达式中使用的 ora:view XQuery 函数。在该示例中,ora:view 获取两个输入参数,即“HR”和“employees”,它们指示该函数查询属于 HR 数据库模式的员工表。因此,ora:view 将返回一个表示 HR.employees 表行的员工 XML 文档序列。但为了节省结果文档中的空间,只将前三个员工记录传递给结果序列。这是通过在 FLWOR 表达式的 where 子句中指定 $i/EMPLOYEE_ID <= 102 而实现的。请注意 FLWOR 表达式的 return 子句中使用的 xs:string()xs:integer() XQuery 类型表达式。实际上,此处使用的这两个 XQuery 表达式不仅将 XML 节点值转换为相应的类型,而且还将提取这些节点值。随后,生成的员工 XML 文档作为 employees.xml 保存到之前在列表 1 中另一个 PL/SQL 过程中创建的 /public/employees XML 信息库文件夹。要确保此操作已完成,可执行以下查询:

SELECT XMLQuery('for $i in fn:doc("/public/employees/employees.xml")

return;

$i'

RETURNING CONTENT) AS RESULT FROM DUAL;

该查询应生成以下输出:





100

King

24000





101

Kochhar

17000





102

De Haan

17000



在以上 XQuery 中,fn:doc XQuery 函数用于访问 Oracle XML DB 信息库中存储的单个 XML 文档。但如果要处理一些具有相同或相似结构的 XML 文档(存储在同一 XML 信息库文件夹中),应该怎么做?这种情况下,另一个用于处理 XML 信息库资源的 XQuery 函数(即 fn:collection)可能会派上用场。本文稍后将介绍几个有关如何使用 fn:collection XQuery 函数的示例。

查询 XMLType 数据

XQuery 使您可以操作基于 XML 模式以及非基于模式的数据。以下示例演示了如何使用 XMLTable 函数从 OE 演示数据库模式中查询基于 PurchaseOrder XML 模式的 XMLType 表。

SELECT ttab.COLUMN_VALUE AS OrderTotal FROM purchaseorder,

XMLTable(

'for $i in /PurchaseOrder

where $i/User = "EABEL"

return;



{$i/Reference}



{fn:sum(for $j in $i/LineItems/LineItem/Part

return ($j/@Quantity*$j/@UnitPrice))}



'

PASSING OBJECT_VALUE

) ttab;

在以上示例中,您在 XMLTable 函数的 PASSING 子句中使用 OBJECT_VALUE 虚拟列将 purchaseorder 表作为上下文项传递给此处使用的 XQuery 表达式。XQuery 表达式计算用户 EABEL 请求的每个购买订单的总计,并为处理的每个订单生成一个 OrderTotal XML 元素。要访问生成的 XML,请使用 SELECT 列表中的 COLUMN_VALUE 虚拟列。最终的输出应如下所示:

ORDERTOTAL

-------------------------------------------------------------



EABEL-20021009123338324PDT

1328.05





EABEL-20021009123335791PDT

2067.15





EABEL-20021009123336251PDT

289.6





EABEL-20021009123336382PDT

928.92

要获得相同的最终结果,可以改用 XMLQuery 函数。但如果将上一个示例中使用的 XQuery 表达式参数传递给 XMLQuery(如下所示):

SELECT XMLQuery('for $i in /PurchaseOrder

where $i/User eq "EABEL"

return

{$i/Reference}



{fn:sum(for $j in $i/LineItems/LineItem/Part

return ($j/@Quantity*$j/@UnitPrice))}



'

PASSING OBJECT_VALUE

RETURNING CONTENT)

FROM purchaseorder;

则 XQuery 表达式返回的空序列将与 purchaseorder 表联接,从而包含在查询总结果集中。实际上,这意味着输出将不仅包含为用户 EABEL 请求的订单生成的 OrderTotal 元素,而且还包含为 purchaseorder 表中存储的所有其他订单生成的空行(默认情况下,purchaseorder 表包含 132 行)。从结果集中排除空行的方法之一是在 SELECT 语句的 WHERE 子句中使用 existsNode SQL 函数,而不是在 XQuery 表达式中使用 WHERE 子句,如下所示:

SELECT XMLQuery('for $i in /PurchaseOrder

return

{$i/Reference}



{fn:sum(for $j in $i/LineItems/LineItem/Part

return ($j/@Quantity*$j/@UnitPrice))}



'

PASSING OBJECT_VALUE

RETURNING CONTENT) AS ordertotal

FROM purchaseorder

WHERE existsNode(OBJECT_VALUE, '/PurchaseOrder[User = "EABEL"]') = 1;

以上查询与本部分开头的 XMLTable 示例生成相同的输出。


相关阅读