【IT168 服务器学院】查询 Oracle XML DB 信息库中的 XML 数据
为访问 Oracle XML DB 信息库中存储的 XML 数据,Oracle XQuery 引入了 fn:doc 和 fn:collection XQuery 函数。使用 fn:doc,您可以查询 XML 信息库中存储的单个 XML 文档,而 fn:collection 使您可以访问同一信息库文件夹中存储的多个 XML 文档。
正如本文之前(参阅使用关系数据构建 XML部分)介绍的示例所演示,使用 fn:doc 非常简单直接。它获取表示信息库文件资源 (URI) 的字符串并返回该 URI 指向的文档。要了解 fn:collection XQuery 函数的作用,同一文件夹中至少应有两个信息库文件。如果已经运行了列表 1 中的代码,则已经创建了 /public/employees 信息库文件夹并在其中存储了 employees.xml 文件。因此,您将需要在该文件夹中至少再创建一个 XML 文件,然后才能试用 fn:collection。列表 2 中的 PL/SQL 代码基于 SCOTT/TIGER 演示数据库模式的 dept 和 emp 表存储的关系数据构建 XML,然后将生成的 XML 文档作为 acc_dept.xml 保存到 /public/employees 信息库文件夹。要运行列表 2 中的 PL/SQL 过程,请确保以 SCOTT/TIGER 的身份登录。
列表 2:基于关系数据构建 XML 并将其保存到 XML 信息库
DECLARE
XMLdoc XMLType;
BEGIN
SELECT XMLQuery(
''for $j in ora:view("SCOTT", "dept")/ROW
where $j/DEPTNO = 10
return ()''
{$j/DEPTNO,
$j/DNAME}{
for $i in ora:view("SCOTT", "emp")/ROW
where $i/DEPTNO = $j/DEPTNO
return ()}
{$i/EMPNO,
$i/ENAME,
$i/SAL}
RETURNING CONTENT) INTO XMLdoc FROM DUAL;
IF(DBMS_XDB.CREATERESOURCE(''/public/employees/acc_dept.xml'', XMLdoc)) THEN
DBMS_OUTPUT.PUT_LINE(''Resource is created'');
ELSE
DBMS_OUTPUT.PUT_LINE(''Cannot create resource'');
END IF;
COMMIT;
END;
/
此时,/public/employees 信息库文件夹应包含两个文件:acc_dept.xml(由列表 2 中的 PL/SQL 代码生成)和 employees.xml 文件(由列表 1 中的代码生成)。由于这些 XML 文档存储在同一信息库文件夹中,因此可以使用 fn:collection 函数访问两个 XML 文档中存储的员工信息。然而,尽管这些 XML 文档均包含员工 XML 元素(这些元素实际上具有相同结构),但 XML 文档本身的结构迥然不同。在 employees.xml 中,文档根元素为 EMPLOYEES,而 acc_dept.xml 将 DEPARTMENT 用作根元素。要解决此问题,可以通过 XQuery 使用 XPath // 构造,从而导航到 XML 文档中的某个节点,而不必指定该节点的确切路径。以下示例演示了如何在 XQuery 表达式中使用 XPath // 构造:
SELECT XMLQuery(
''for $i in fn:collection("/public/employees")//EMPLOYEE
where $i/SAL >= 5000
order by $i/ENAME
return;
$i''
RETURNING CONTENT) FROM DUAL;
该构造应生成以下输出:
102 De Haan 17000 7839 KING 5000 100 King 24000 101 Kochhar 17000
您可以看到,以上输出包含从 employees.xml 和 acc_dept.xml 中获取的员工 XML 元素,这些元素表示薪酬大于或等于 5,000 美元的员工。
将 XML 分解为关系数据
如果应用程序处理关系数据而非 XML,而您需要访问的数据以 XML 格式存储,则将 XML 分解为关系数据可能会非常有用。继续进行上一部分的示例,您可以使用 SQL 函数 XMLTable 将员工 XML 元素分解为虚拟表的单个列,如下所示:
SELECT emps.empno,emps.ename, emps.sal FROM
XMLTable(
''for $i in fn:collection("/public/employees")//EMPLOYEE
where $i/SAL >= 5000
return;
$i''
COLUMNS empno NUMBER PATH ''/EMPLOYEE/EMPNO'',
ename VARCHAR2(30) PATH ''/EMPLOYEE/ENAME'',
sal NUMBER PATH ''/EMPLOYEE/SAL'') emps;
该查询将生成以下输出:
EMPNO ENAME SAL
----- -------------- ----------
7839 KING 5000
100 King 24000
101 Kochhar 17000
102 De Haan 17000
查询外部数据源
使用 XQuery,可以基于 XML 数据以及可以用 XML 表示的非 XML 数据生成 XML 文档,无论其位置如何:无论是存储在数据库中、置于网站上、即时创建还是存储在文件系统中。但要注意,Oracle XML DB 为针对数据库中存储的数据进行的 XML 操作提供了非常高的性能和可伸缩性。因此,如果您能够完全控制所处理的数据,则最好将它移动到数据库中。
正如您从前面的示例中了解到的,在 Oracle XQuery 实施中,doc 和 collection XQuery 函数用于访问 Oracle XML DB 信息库中存储的 XML 文档。可以通过 XMLTable 和 XMLQuery SQL 函数中的 PASSING 子句动态绑定外部数据源。考虑以下示例。假设您的公司要为那些致力于 XQ 项目的员工支付奖金。因此,财务部发布了 empsbonus.xml 文件,其中包含有资格获得奖金的员工列表以及该列表中输入的每个员工的奖金数额。empsbonus.xml 文件可能如下所示:
100 1200 101 1000
在实际情况中,以上的 XML 文件可能置于网站上(因此可以通过互联网获得)、以文件形式存储在本地文件系统中,或以文件资源形式存储在 Oracle XML DB 信息库中。就本示例而言,该文件位于网站上。为简单起见,可以在目录(Web 服务器在其中存储可从 Web 看到的文档)中创建一个员工文件夹,然后在该文件夹中插入 empsbonus.xml 文件,以便可以通过以下 URL 访问 empsbonus.xml 文件:
http://localhost/employees/empsbonus.xml
接下来,假设您需要基于 empsbonus.xml 文档中存储的数据创建一个报表。在该报表中,您可能不但要包含列表中显示的奖金数额以及每个员工的员工 ID,还要包含他/她的全名。因此,可以首先使用以下查询生成一个新的 XML 文档(假设您以 HR/HR 的身份连接):
SELECT XMLQuery(
''for $k in 1
return ({for $i in ora:view("employees")/ROW, )''
$j in $emps/EMPLOYEES/EMPLOYEE
where $i/EMPLOYEE_ID = $j/EMPNO
return ()} {xs:string($i/EMPLOYEE_ID)} {xs:string(fn:concat($i/FIRST_NAME, " ", $i/LAST_NAME))} {xs:integer($j/BONUS)}
PASSING xmlparse (document httpuritype
(''http://localhost/employees/empsbonus.xml'').getCLOB()) as "emps"
RETURNING CONTENT).getStringVal() as RESULT FROM DUAL;