IBM®
跳转到主要内容
    中国 [选择]    使用条款
 
 
Select a scope:Search for:    
    首页    产品    服务与解决方案     支持与下载    个性化服务    
跳转到主要内容

developerWorks 中国  >  Information Management | XML  >

使用 DB2 pureXML 分解 XML 文档

DB2 for Linux, UNIX, and Windows 中的两种 XML 分解方法

developerWorks
文档选项

未显示需要 JavaScript 的文档选项

样例代码

英文原文

英文原文


级别: 中级

Salvador Ledezma, 资深软件工程师, IBM
Bert Van Der Linden (robbert@us.ibm.com), DB2 pureXML 架构师, IBM

2008 年 3 月 06 日

从 9.1 版开始,DB2 提供了对存储、管理和搜索 XML 数据的全新支持。其中一个新特性就是带标注的 XML 模式分解(annotated XML schema decomposition)。通过带标注的 XML 模式分解,可以将 XML 分解到关系表中。另一种分解 XML 文档的方法是通过 SQL 或 XML 函数:XMLTABLE。本文考察这两种分解 XML 数据的方法,包括如何使用 XMLTABLE 函数进行分解。本文还将对带标注的 XML 模式分解和 XMLTABLE 分解进行比较,并介绍这两种分解的建议用法。

简介

随着企业中的 XML 数据不断增长,不可能始终将 XML 数据以 XML 形式存储。也许您正在使用遗留的数据架构,或者其他的需求使您只能使用关系存储。实际上,发送和接收 XML 数据形式的消息并不少见,而这些消息由关系数据构建而来,并可以分解为关系数据。当不能将数据存储为 XML 时,DB2 的两个特性可以提供帮助:SQL/XML 发布函数和 XML 分解。首先,SQL/XML 发布函数可以帮助使用关系数据构建 XML 数据。本文对此不予讨论。欲获得对于这些函数的介绍,以及关于如何查询 DB2 中的 XML 数据的常用信息,请参阅 developerWorks 文章 “用 SQL 查询 DB2 XML 数据” (developerWorks,2006 年 3 月)。

本文只关注 DB2 中 XML 数据的 “分解” 方法。分解是指将 XML 元素和属性映射到关系表和列的过程。在 DB2 中,一种分解方法是通过使用带标注的 XML 模式。如果 XML 数据包含一个 XML 模式,那么这是最容易、最快速的分解方式。如果这种映射比较复杂,并且涉及多个表,那么可以使用已有的工具自动完成映射和分解步骤。

另一种较鲜为人知的分解方法是使用 SQL/XML 函数 XMLTABLE。当不存在 XML 模式时,这种方法很有用。使用 XMLTABLE 函数可能更加复杂,因为必须手动编写分解步骤。这意味着开发人员必须使用 XQuery 表达式明确说明如何将特定的 XML 元素映射到一个表和列。然而,正是这种灵活性使得 XMLTABLE 分解比带标注的 XML 模式分解更强大,使它能执行带标注的 XML 模式分解所不能执行的一些映射。

本文展示既使用带标注的 XML 模式,又使用 XMLTABLE 函数的分解例子。另外还展示带标注的 XML 模式分解不支持而 XMLTABLE 支持的一些例子。最后,本文对每种方法的最佳实践作一个比较,并给出一些推荐用法。





回页首


带标注的 XML 模式分解

DB2 中的带标注的 XML 模式分解特性可用于将 XML 文档分解到关系表。顾名思义,它使用 XML 模式中的标注作为映射语言,将 XML 文档中的信息映射到关系表。由于需要有一个 XML 模式,所以必须将 XML 模式文档存储在 DB2 XML Schema Repository(XSR) 中,并指定为可分解。将 XML 文档分解到映射的关系列中,这项工作可通过一个 DB2 存储过程调用或一个命令行处理器(CLP)命令完成。

标注 XML 模式的一种方法是使用 IBM Data Studio。Data Studio 是一个可免费下载的、全面的集成开发环境,可用于创建、编辑、调试、部署和测试 DB2 数据库应用程序,包括开发存储过程和用户定义函数。欲获得下载信息,请参阅本文的 参考资料 小节。Data Studio 的一个组件是带标注的 XML 模式分解映射编辑器(Mapping Editor)。该组件有一个简单而直观的图形化界面,通过它可以映射 XML 模式与关系模式之间的关系。在将 XML 元素或属性图形化地映射到 DB2 中的关系列时,将自动标注 XML 模式文档。当保存 XML 模式并将其注册到 XSR 中之后,就可以将 XML 文档分解到 DB2 中。

xdbDecompXML 是一个用于带标注的 XML 模式分解的 DB2 存储过程。xdbDecompXML 有若干个版本,每个版本专门针对不同大小的文档进行了优化。这些版本包括:

  • XdbDecompXML
  • XdbDecompXML10MB
  • XdbDecompXML25MB
  • XdbDecompXML50MB
  • XdbDecompXML75MB
  • XdbDecompXML100MB

顾名思义,每个存储过程只是在要进行分解的 XML 文档的大小上有区别。例如,对于不超过 1 MB 的 XML 文档,使用 xdbDecompXML,对于不超过 10MB 的 XML 文档,使用 xdbDecompXML10MB。所有这些存储过程都带 8 个参数,其中有 3 个参数是保留参数,必须设为 NULL。这些参数是:

  • rschema
    由两部分组成的 XSR 对象名称(在 XML 模式库中注册)的 SQL 模式部分。

  • xmlschemaname
    由两部分组成的 XSR 对象名称的 XML 模式名。

  • xmldoc
    作为 BLOB 对象传入的要分解的 XML 文档。该文档的大小决定应该调用哪个存储过程。

  • documentid
    要分解的 XML 文档的标识符。可以在 XML 模式中的特定标注中,尤其是在 db2-xdb:expression 和 db2-xdb:condition 中使用该标识符。

  • validation
    一个整数,表明是否还应该根据 XML 模式对要分解的 XML 文档进行验证。0 表示不需要验证,1 表示需要验证。

  • 保留参数
    最后 3 个参数被保留,必须设为 NULL。

根据存储在 XSR 中的 XML 模式中的标注,xdbDecompXML 调用使 DB2 分解 XML 文档并将其插入到适当的关系表中。

对 XML 模式标注和这组 xdbDecompXML 存储过程的详细讨论超出了本文的范围。要想更详细地了解带标注的 XML 模式分解,包括诸如基于内容的条件分解或指定在插入前应用的内容转换等高级特性,请参阅 “DB2 Version 9 XML Guide” 出版物(参见本文后面的 参考资料 小节)。对于熟悉 XML Extender 及其分解方法的读者,请参阅 “From DAD to Annotated XML Schema Decomposition”(developerWorks,2006 年 4 月),以了解更多信息。





回页首


XMLTABLE 分解

虽然带标注的 XML 模式分解要求 XSR 中存在 XML 模式,但是可以使用 XMLTABLE 函数分解没有 XML 模式的文档。XMLTABLE 是一个 SQL 表函数,它通过计算 XQuery 表达式返回一个表。返回的表可以包含任意 SQL 数据类型的列,包括 XML。可以将变量传递到 XMLTABLE 中指定的 XQuery 表达式中。

XMLTABLE 的常见语法如下所示:


清单 1. XMLTABLE 的常见语法
                
XMLTABLE( xquery-expression  PASSING xml-source 
COLUMNS 
column-name	column-(sql)data-type PATH path-xquery-expression
,...)

xml-source 是为结果表提供数据的 XML 文档。它可以存储在 DB2 中的一个 XML 列中,也可以是 DB2 之外的一个文档。xquery-expression 参数中指定的 XQuery 表达式是一个可生成行的 XQuery 表达式。对于输出序列中的每一项,都生成一个行。结果表的结构由函数的 COLUMNS 子句定义。在这个子句中,可以通过指定列的名称(column-name)、数据类型(column-(sql)data-type)以及如何生成列值,定义列的特征。结果表的列值可以通过在 XMLTABLE 中的 PATH 子句中指定一个 XQuery 表达式(path-xquery-expression)来生成。path-xquery-expression 根据 xquery-expression 选择的项指定列值。要了解更多信息,请参阅下面列出的例子。

通过使用 XMLTABLE,并结合一个 INSERT 语句,可以将从 XML 文档中取出的值插入到一个关系表中,完成与带标注的 XML 模式分解相同的功能。这通常被称作 “Insert-from-XMLTABLE” 语句。本文使用术语 XMLTABLE 分解。

传给 INSERT 语句的数据并不限于从 XMLTABLE 函数返回的结果。它可以来自其他地方,例如主机变量、其他表、其他 XMLTABLE 语句甚至其他表函数。使用 XQuery 和 SQL 的威力可以使 XMLTABLE 分解变得非常灵活。一个 INSERT 语句仍然只可以插入到一个表。如果一个映射需要插入到多个表,那么必须定义多个包含 XMLTABLE 调用的 INSERT 语句。因此,将 XMLTABLE 分解语句放到一个 SQL 存储过程中会有所帮助。通过将语句定义在一个 SQL 存储过程中,可以使应用程序只进行一次调用,而不管某个特定映射需要填充多少个表。SQL 存储过程还可以带来附加的性能优势,因为要分解的 XML 文档即使要在多个 XMLTABLE 函数调用中用到,也只需解析一次。





回页首


XML 分解:一个例子

现在来看一个关于如何使用带标注的 XML 模式分解和 XMLTABLE 分解来分解 XML 文档的例子。

示例 XML 数据

假设您有一些表示邮件信息的 XML 数据。一个典型的文档可能类似以下内容:


清单 2. 表示邮件信息的示例 XML 数据
                
<email:mails xmlns:email="http://mymail.com/mails">
   <mail>
	<envelope>
         <from></from>
	   <to></to>
	   <email:Date></email:Date>
	   <subject></subject>
	</envelope>
	<body></body>
	<attachment></attachment>
   </mail>
   <mail>
	...
   </mail>
	...
</email:mails>

每个 <mail> 消息包含一个信封、一个主体和一个附件,这个 XML 文档可以有无限多个 <mail> 元素。为了简化问题,假设附件是文本数据,而不是二进制数据。

关系模式

很多时候,在执行分解时,不能对已有的关系模式加以控制。假设要将文档分解到 3 个关系表中。这 3 个表定义如下:


清单 3. 关系模式的 DDL
                
create table envelopext (
docID 		integer not null generated always as identity primary key, 
mailfrom 	varchar(100), 
mailto		varchar(100), 
maildate 	varchar(30), 
subject 	varchar(100)
);

create table bodyxt (
bodyId		integer not null generated always as identity primary key,
body		varchar(30000)
);

create table attachxt (
attachId	integer not null generated always as identity primary key,
attachment	varchar(100)
);





回页首


使用带标注的 XML 模式分解来分解 XML 数据并将其插入到关系表中

最后,假设有一个已使用所需映射进行了标注的 XML 模式。如前所述,现有的一些工具,例如 Data Studio Mapping Editor,可以通过自动生成标注来完成映射。(要获得包含完整的标注 XML 模式,请查看 下载 小节。)

如果您使用的是一个 Java 应用程序,那么只需使用 JDBC 和一个 Java InputStream 调用 DB2 分解存储过程 xdbDecompXML,直接将 XML 文件的内容传递给存储过程。如清单 4 所示:


清单 4. 调用 xdbDecompXML 存储过程
                
String sql = "{CALL xdbDecompXML(?,?,?,?,?,NULL,NULL,NULL)}";
CallableStatement cstmt = con.prepareCall( sql );

String filename = "mail.xml";
File currFile = new File( filename );
long length = currFile.length();

// SQL Schema Name
// assume NULL
cstmt.setNull( 1, Types.VARCHAR );

// XML Schema Name
cstmt.setString( 2, "\"TEST001\"" );

// XML document to be decomposed, represented as a BLOB
BufferedInputStream bis = 
new BufferedInputStream( new FileInputStream( filename ) );
cstmt.setBinaryStream( 3, bis, ( int ) length );

// XML Document identifier
cstmt.setString( 4, "TEST001" );

// Options to validate the instance document against the XML Schema
int validate = 0;
cstmt.setInt( 5, validate );

/*
The last 3 parameters are reserved for future use.  
Currently NULL values are expected and must be passed in
*/

cstmt.execute();
con.commit();

cstmt.close(); 

对于应用程序,它只是发出一个标准的存储过程调用。由于 XML 模式之前已被标注,并注册在 XSR 中,所以存储过程只需获得 XML 文档,并将其分解到 3 个映射表中。





回页首


用 XMLTABLE 分解来分解 XML 数据并将其插入到关系表中

为了展示 XMLTABLE 分解也可以完成同样的操作,这里使用 XMLTABLE 和一个 SQL INSERT 语句来处理同样的 XML 文档和一组关系表。将 XMLTABLE 分解函数放到一个 SQL 存储过程中(这不是必需的)。从应用程序的角度看,使用 XMLTABLE 分解只需调用一个存储过程,就像带标注的 XML 模式分解一样。

在考虑这个存储过程之前,先编写调用它的 Java 代码。如清单 5 所示:


清单 5. 调用使用 XMLTABLE 的存储过程
                
// sql stored procedure
String sql = "{CALL XMLTINSERT(?)}";
		
CallableStatement cstmt = con.prepareCall( sql );
String filename = "mail.xml";

File currFile = new File( filename );
long length = currFile.length();
		
BufferedInputStream bis = 
new BufferedInputStream( new FileInputStream( filename ) );

cstmt.setBinaryStream( 1, bis, ( int ) length );
cstmt.execute();
con.commit();
cstmt.close();


该存储过程被定义为有一个 XML 类型的参数。否则,它就和 xdbDecompXML 调用非常相似。同样,这里定义一个 Java InputStream,以便直接将一个 XML 文件的内容传递给存储过程。

清单 6 显示了上面的例子使用的包含 XMLTABLE 语句的 SQL 存储过程:


清单 6. 使用 XMLTABLE 分解的存储过程
                
CREATE PROCEDURE XMLTINSERT (IN db2xml XML)
	SPECIFIC XMLTINSERT
------------------------------------------------------------------------
-- SQL Stored Procedure 
-- db2xml is an IN parameter of type XML 
------------------------------------------------------------------------
P1: BEGIN

-- TABLE ENVELOPEXT
INSERT INTO ENVELOPEXT (MAILFROM, MAILTO, MAILDATE, SUBJECT)
SELECT MAILFROM, MAILTO, MAILDATE, SUBJECT
FROM
XMLTABLE(XMLNAMESPACES(
'http://www.sal.com/mails' AS "email"),
'$doc/email:mails/mail'
PASSING  DB2XML AS "doc" 
	COLUMNS 
MAILFROM 	VARCHAR (100)	PATH 'envelope/from',
MAILTO 		VARCHAR (100) 	PATH 'envelope/to',
MAILDATE 	VARCHAR (30) 	PATH 'envelope/email:Date',
SUBJECT 	VARCHAR (100) 	PATH 'envelope/Subject') AS T;
	    
-- TABLE BODYXT
INSERT INTO BODYXT (BODY)
SELECT BODY
FROM
XMLTABLE(XMLNAMESPACES(
'http://www.sal.com/mails' AS "email"),
'$doc/email:mails/mail'
PASSING  DB2XML AS "doc" 
	COLUMNS 
BODY 		VARCHAR (30000)	PATH 'body') AS T;
	
-- TABLE ATTACHXT
INSERT INTO ATTACHXT (ATTACHMENT)
SELECT ATTACHMENT
FROM
XMLTABLE(XMLNAMESPACES(
'http://www.sal.com/mails' AS "email"),
'$doc/email:mails/mail'
PASSING  DB2XML AS "doc" 
	COLUMNS 
ATTACHMENT 	VARCHAR (100) PATH 'attachment/@email:name') AS T;

END P1%


注意,这里有 3 个单独的 INSERT 语句。这是因为有 3 个单独的表,对于每个表都需要单独调用一个 XMLTABLE 语句。现在来详细分析第一个 INSERT 语句:

INSERT INTO ENVELOPEXT (MAILFROM, MAILTO, MAILDATE, SUBJECT)

这是标准的 INSERT 语法。这将把数据插入到 ENVELOPEXT 表的 MAILFROM、MAILTO、MAILDATE 和 SUBJECT 列中。接下来是 INSERT 的源:

SELECT MAILFROM, MAILTO, MAILDATE, SUBJECT
FROM

插入的数据来自一个 SELECT 语句。这样将从 MAILFROM、MAILTO、MAILDATE 和 SUBJECT 列获取数据。接下来,事情变得有趣起来。SELECT 中的数据来自 XMLTABLE 函数的结果:

XMLTABLE(XMLNAMESPACES(
'http://www.sal.com/mails' AS "email"),
'$doc/email:mails/mail'
PASSING  DB2XML AS "doc"

XMLTABLE 期望一个 XQuery 表达式作为参数。在这个例子中,XQUERY 表达式是:

'$doc/email:mails/mail'

这个稍后详述,先看看 XML 数据从哪里来:

PASSING  DB2XML AS "doc" 

DB2XML 提供 XML 源。在这里,DB2XML 是存储过程的 IN 参数,即一个 XML 值。当计算 XQuery 表达式时,会提供一个 XQuery 变量,其值等于被传递的那个参数(DB2XML),其名称由 AS 子句指定(doc)。因此,'$doc/email:mails/mail' 引用 XML 文档中由该路径定义的元素序列。

最后还有一点要说明。注意,该路径中有一个名称空间。必须告诉 DB2 这个名称空间是在引用什么。XMLNAMESPACES 函数可以帮忙实现这一点:

XMLNAMESPACES('http://www.sal.com/mails' AS "email")

XMLNAMESPACES 函数将名称空间提供给 XMLTABLE 中使用的 XQuery 表达式的静态上下文,所以各个不同的 XQuery 表达式不必在它们的 prolog 中包括或重复名称空间声明。虽然也可以在 XQuery prolog 中声明名称空间,但是与通过 XQuery prolog 使用名称空间声明相比,使用 XMLNAMESPACES 的优雅之处在于,名称空间可以在 XMLTABLE 参数中的所有 XQuery 表达式中使用,避免了重复编写代码。

表函数返回一个表的列。可以用 COLUMNS 子句确定这些列:

	COLUMNS 
MAILFROM 	VARCHAR (100)	PATH 'envelope/from',
MAILTO 		VARCHAR (100) 	PATH 'envelope/to',
MAILDATE 	VARCHAR (30) 	PATH 'envelope/email:Date',
SUBJECT 	VARCHAR (100) 	PATH 'envelope/Subject') AS T;

每个 PATH 子句本身也是一个 XQuery 表达式,但是它不是对整个 XML 文档进行操作,而是对 XML 源子句($doc/email:mails/mail')中定义的内容进行操作。所以,路径 envelope/From 是 <mail> 元素中的一个路径。MAILFROM 列被定义为一个 VARCHAR(100),其内容来自完整的 XML 路径 $doc/email:mails/mail/envelope/from。其他列也以类似的方式填充。

XMLTABLE 结果被插入到 ENVELOPEXT 表中。其他两个 XMLTABLE 语句也类似地分别插入到 BODYXT 和 ATTACHXT 表。而且,虽然要执行 3 个单独的 XMLTABLE 语句,但是 XML 文档是存储过程中的一个变量,因此只需解析一次。

最后,回忆一下最初的 XML 文档的样子。对于每个 <mail> 元素,XMLTABLE 语句返回一行。由于可能存在多个 <mail> 元素,调用一次存储过程可能会在表中创建多个行。因此,如果有 20 个 <mail> 元素,那么存储过程就会插入 20 个行。

如果存储过程保存在一个名为 xtinsert.db2 的文件中,那么,为了在 DB2 中创建该存储过程,您或 DB2 管理员可以在 CLP 上运行下面的命令,然后应用程序就可以使用该存储过程了:

db2 -td% -vf xtinsert.db2





回页首


XML 分解的一些特例

下面讨论不能通过带标注的 XML 模式进行分解的一些 XML 文档。由于 XMLTABLE 分解是由 XQuery 表达式驱动的,因此可以使用它编程式地映射这些特例。

使用 XBRL 数据映射多对多子元素关系

从技术的角度看,可扩展商业报告语言(eXtensible Business Reporting Language,XBRL)的有趣之处在于它定义元素关系的方式。这些关系代表着现实世界中的业务流程。

考虑清单 7 中这个简单的 XBRL 文档:


清单 7. 示例 XBRL 数据
                
<?xml version="1.0" encoding="utf-8"?>
<linkbase 
   xmlns="http://www.xbrl.org/2003/linkbase" 
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
   xmlns:xlink="http://www.w3.org/1999/xlink" 
   xsi:schemaLocation="http://www.xbrl.org/2003/linkbase xbrl-linkbase-2003-12-31.xsd">
                                                     
  <calculationLink 
	xlink:type="extended" 
	xlink:role=http://www.xbrl.org/2003/role/link 
	xlink:title="Calculations, All">

    <loc 
	xlink:type="locator" 
	xlink:href="BasicCalculation.xsd#ci_TotalPropertyPlantEquipment" 
	xlink:label="ci_TotalPropertyPlantEquipment" />

    <loc 
	xlink:type="locator" 
	xlink:href="BasicCalculation.xsd#ci_Land" 
	xlink:label="ci_Land" />

    <loc 
	xlink:type="locator" 
	xlink:href="BasicCalculation.xsd#ci_Building" 
	xlink:label="ci_Building" />

    <loc 
	xlink:type="locator" 
	xlink:href="BasicCalculation.xsd#ci_FurnitureFixtures" 
	xlink:label="ci_FurnitureFixtures" />

    <loc 
	xlink:type="locator" 
	xlink:href="BasicCalculation.xsd#ci_ComputerEquipment" 
	xlink:label="ci_ComputerEquipment" />

    <loc 
	xlink:type="locator" 
	xlink:href="BasicCalculation.xsd#ci_Other" 
	xlink:label="ci_Other" />

    <calculationArc 
	xlink:type="arc" 
	xlink:arcrole="http://www.xbrl.org/2003/arcrole/summation-item" 
	xlink:from="ci_TotalPropertyPlantEquipment" 
	xlink:to="ci_Land" order="1" weight="1" use="optional" />

    <calculationArc 
	xlink:type="arc" 
	xlink:arcrole="http://www.xbrl.org/2003/arcrole/summation-item" 
	xlink:from="ci_TotalPropertyPlantEquipment" 
	xlink:to="ci_Building" order="2" weight="1" use="optional" />

    <calculationArc 
	xlink:type="arc" 
	xlink:arcrole="http://www.xbrl.org/2003/arcrole/summation-item" 
	xlink:from="ci_TotalPropertyPlantEquipment" 
	xlink:to="ci_FurnitureFixtures" order="3" weight="1" use="optional" />

    <calculationArc 
	xlink:type="arc" 
	xlink:arcrole="http://www.xbrl.org/2003/arcrole/summation-item" 
	xlink:from="ci_TotalPropertyPlantEquipment" 
	xlink:to="ci_ComputerEquipment" order="4" weight="1" use="optional" />

    <calculationArc 
	xlink:type="arc" 
	xlink:arcrole="http://www.xbrl.org/2003/arcrole/summation-item" 
	xlink:from="ci_TotalPropertyPlantEquipment" 
	xlink:to="ci_Other" order="5" weight="1" use="optional" />

  </calculationLink>
</linkbase>


XBRL:可扩展商业报告语言

XBRL 是用于以电子化方式通信企业和财务数据的一种 XML 语言。作为一种新兴的开放标准,它主要用于收集和报告财务信息。XBRL 可以显示各项数据之间有何关联,从而体现各项财务数据是如何计算的,以及它们是否提供给用于组织或演示目的的特定分组中。要了解更多信息,请参阅本文的 参考资料 小节。

XBRL 增加了更多的语义,从而实现了 XML 增强。一个关键增强是,使用 “弧线” 来连接两个项。本文感兴趣的是 <calculationArc> 元素。这种元素定义两个资产之间的关系。该元素的 “arcrole” 属性基于 URI,它告诉您这个弧表示 “to” 和 “from” 属性之间的合计(summation)关系。每个 "to" 属性标识一个单独的项,例如 “ci_Land” 和 “ci_Building”。这些项的和组成了 “from” 属性的值,在这里就是 “ci_TotalPropertyPlantEquipment”。

属性(例如 “ci_Land” 和 “ci_Building”)实际上就是标签。<loc> 元素可以帮助识别每个标签代表什么。例如 <loc> 元素,它的 “label” 属性等于 “ci_Land”:

<loc 
	xlink:type="locator" 
	xlink:href="BasicCalculation.xsd#ci_Land" 
	xlink:label="ci_Land" />

可以看到,有一个指向 “BasicCalculation.xsd” XML 模式文档的 “href” 属性。仔细观察该文档,可以发现实际的值是 “ci_land”。

对 XBRL 数据的完整分析超出了本文的范围,但是可以想象一下,您可能需要将这个 XML 文档分解到一个 “arc” 表中,这个表存储 “from” 和 “to” 属性。但是您要的不是标签,而是提供实际值的引用的名称。

这个表包含 3 列:一个自动生成的 ARCID,一个 ARCFROM,还有一个 ARCTO 列,如清单 8 所示:


清单 8. ARC 表的 DDL
                
CREATE TABLE ARC (
  ARCID		integer not null generated always as identity primary key,
  ARCFROM	varchar(100),
  ARCTO		varchar(100)
);

为便于分解,<calculationArc> 元素中的 “from” 和 “to” 属性值被 <loc> 元素中相应的 “href” 属性替代。例如,对于下面的元素:

<calculationArc 
	xlink:type="arc" 
	xlink:arcrole="http://www.xbrl.org/2003/arcrole/summation-item" 
	xlink:from="ci_TotalPropertyPlantEquipment" 
	xlink:to="ci_Land" order="1" weight="1" use="optional" />

<loc> 元素中的值 BasicCalculation.xsd#ci_TotalPropertyPlantEquipmentBasicCalculation.xsd#ci_Land 分别被插入到 ARC 表的 ARCFROM 和 ARCTO 列中。这两个值对应于 <calculationArc> 元素中的 from="ci_TotalPropertyPlantEquipment"to="ci_Land" 标签。表中的其他行也以类似的方式填充。

带标注的 XML 模式分解不支持这种类型的分解,因为这是一个多对多的多个子元素的映射,需要根据动态的值生成行。这种情况下可以使用 XMLTABLE。如前所述,XQuery 是隐藏在 XMLTABLE 函数背后的引擎,它使 XMLTABLE 成为一个非常强大的工具。

清单 9 是一个按要求执行分解的存储过程:


清单 9. 使用 XMLTABLE 进行 XBRL 分解的 SQL 存储过程
                
CREATE PROCEDURE XMLTINSERT2 ( IN db2xml XML )
	SPECIFIC XMLTINSERT2
------------------------------------------------------------------------
-- SQL Stored Procedure 
-- db2xml 
------------------------------------------------------------------------
P1: BEGIN
INSERT INTO ARC
	(ARCFROM,
	ARCTO) 
SELECT	
	ARCFROM,
	ARCTO 
	FROM XMLTABLE(
	'$doc/*:linkbase/*:calculationLink/*:calculationArc' 
		PASSING DB2XML AS "doc"
		COLUMNS
ARCFROM VARCHAR(100) PATH 'let $x := . return $x/../*:loc[@*:label=$x/@*:from]/@*:href',
ARCTO 	VARCHAR(100) PATH 'let $x := . return $x/../*:loc[@*:label=$x/@*:to]/@*:href'
	)AS T;	
END P1%


首先,使用下面的 XQuery 表达式将上下文选定为 <calculationArc> 元素:

'$doc/*:linkbase/*:calculationLink/*:calculationArc'

注意,最初的 XBRL 文档包含多个不同的名称空间。在 XQuery 表达式中无需关心这一点,因此用一个星号(*)通配名称空间。如果有多个具有不同名称空间的 <calculationArc> 元素,这样做可能产生问题,不过本文示例不会出现此问题。

从这里可以找到想要的一切。看看 COLUMNS 子句定义的 PATH。要得到 ARCFROM,需要其 “label” 属性与当前计算的 “from” 属性匹配的元素。为执行这个测试,使用 let XQuery 关键字将当前的上下文节点分配给一个变量 $x。最后,从当前上下文中导航到 <loc> 元素,并返回其 “label” 属性与 “from” 属性相匹配的 ‘href’ 属性:

'let $x := . return $x/../*:loc[@*:label=$x/@*:from]/@*:href'

可以用相同的方法获得 ARCTO 的值。调用存储过程后得到的分解后的数据如表 1 所示:


表 1. ARC 表中分解后的数据
ARCIDARCFROMARCTO
1BasicCalculation.xsd#ci_TotalPropertyPlantEquipmentBasicCalculation.xsd#ci_Land
2BasicCalculation.xsd#ci_TotalPropertyPlantEquipmentBasicCalculation.xsd#ci_Building
3BasicCalculation.xsd#ci_TotalPropertyPlantEquipmentBasicCalculation.xsd#ci_FurnitureFixtures
4BasicCalculation.xsd#ci_TotalPropertyPlantEquipmentBasicCalculation.xsd#ci_ComputerEquipment
5BasicCalculation.xsd#ci_TotalPropertyPlantEquipmentBasicCalculation.xsd#ci_Other

包含递归元素的 XML 文档

清单 10 中是另一种常见的 XML 文档:


清单 10.表示原料清单的 XML 文档
                
<?xml version="1.0" encoding="UTF-8"?>
<items>
   <item desc="computersystem" model="L1234123">
	  <part desc="computer" partnum="5423452345">
		<part desc="motherboard" partnum="5423452345">
			<part desc="CPU" partnum="6109486697">
				<part desc="register" partnum="6109486697"/>
			</part>
			<part desc="memory" partnum="545454232">
				<part desc="transistor" partnum="6109486697"/>
			</part>
		</part>
		
		<part desc="diskdrive" partnum="6345634563456">
			<part desc="spindlemotor" partnum="191986123"/>
		</part>
		<part desc="powersupply" partnum="098765343">
			<part desc="powercord" partnum="191986123"/>
		</part>
	  </part>
	
	  <part desc="monitor" partnum="898234234">
		<part desc="cathoderaytube" partnum="191986123"/>
	  </part>
	
	  <part desc="keyboard" partnum="191986123">
		<part desc="keycaps" partnum="191986123"/>
	  </part>
	
	  <part desc="mouse" partnum="98798734">
		<part desc="mouseball" partnum="98798734"/>
	  </part>
   </item>
</items>

这种类型的 XML 文档常被称作 “原料清单”。这个示例文档中只包含一个项,但实际上它可以包含无限多个项。这个文档的有趣之处在于 <part> 元素是递归的。<item> 元素可以包含 <part> 子元素,而 <part> 元素又可以包含更多的 <part> 子元素,依此类推,没有限制。

将这个文档分解到一个包含 ITEMNAME、PART 和 PARENT 这几列的 ITEM 表中。


清单 11. ITEM 表的 DDL
                
CREATE TABLE ITEM (
  PID		integer not null generated always as identity primary key,
  ITEMNAME	varchar(25),
  PART		varchar(25),
  PARENT	varchar(25)
);


带标注的 XML 模式分解不允许在 XML 模式中使用递归的元素定义。在这种情况下,同样可以使用 XMLTABLE 分解。清单 12 给出了存储过程:


清单 12. 分解原料清单的 SQL 存储过程
                
CREATE PROCEDURE XMLTINSERT3 ( IN db2xml XML )
	SPECIFIC XMLTINSERT3
------------------------------------------------------------------------
-- SQL Stored Procedure 
-- db2xml 
------------------------------------------------------------------------
P1: BEGIN
INSERT INTO ITEM
	(ITEMNAME,
	PART,
	PARENT) 
SELECT 	
	A.ITEMNAME,
	B.PART,
	B.PARENT
	FROM 
	XMLTABLE('$doc/items/item' PASSING DB2XML AS "doc"
	COLUMNS
		ITEMNAME 	VARCHAR(25)	PATH './@desc',
		ITEM		XML			PATH '.'
	)AS A,
	XMLTABLE('$doc//part' PASSING A.ITEM AS "doc"
	COLUMNS
		PART 		VARCHAR(30) PATH './@desc',
		PARENT		VARCHAR(35) PATH '../@desc'
	)AS B;	
END P1%


在这个例子中,INSERT 语句有两个源:两个 XMLTABLE 语句。第一个语句使用 $doc/items/item 的上下文节点提供项的名称。它还获取 item 子树以便在第二个语句中使用。

第二个语句包含一个 XQuery 表达式,该表达式只从当前的 <item> 子树(A.ITEM)中选择所有的 <part> 元素,不管它们来自哪里($doc//part)。

现在有了所有的 <part> 元素,接着可以得到当前节点描述,并通过向上导航得到父节点描述。表 2 包含调用存储过程后 ITEM 表的内容:


表 2. ITEM 表中分解后的数据
ITEMIDITEMNAMEPARTPARENT
1computersystemcomputercomputersystem
2computersystemmotherboardcomputer
3computersystemCPUmotherboard
4computersystemregisterCPU
5computersystemmemorymotherboard
6computersystemtransistormemory
7computersystemdiskdrivecomputer
8computersystemspindlemotordiskdrive
9computersystempowersupplycomputer
10computersystempowercordpowersupply
11computersystemmonitorcomputersystem
12computersystemcathoderaytubemonitor
13computersystemkeyboardcomputersystem
14computersystemkeycapskeyboard
15computersystemmousecomputersystem
16computersystemmouseballmouse

通过查看 PARENT 列,很容易明白各部件之间的层次。例如,第 4 行是 “register” 部件。查看其父元素,可以得到 “CPU”。“CPU” 的父元素是 “motherboard” 。“motherboard” 的父元素是 “computer”。“computer” 的父元素是 “computersystem”,后者是顶级项。





回页首


性能

FpML:金融产品标记语言

金融产品标记语言(Financial products Markup Language,FpML)是用于复杂的金融产品的一个行业标准协议。FpML 可用于表示场外交易(over-the-counter,OTC)金融衍生交易,这种交易是双方私下进行的(相对于公共交易),其定价要根据其他资产确定。这些交易已成为当今金融市场的重要组成部分,并且成为交易公司进行技术投资的主要动力,因为这些公司承担了大量风险。

此处性能比较中使用的例子基于 2005 年 7 月 14 日发布的 FpML 4.1 Recommendation Specification。要了解更多信息,请参阅 参考资料 小节。

虽然没有进行过仔细的性能研究,不过非正式的研究表明,带标注的 XML 模式分解和 XMLTABLE 分解各有优势。在此,我们来分享一些非正式研究的成果。

简单的映射

我们使用一个增强的、专用的 FpML XML 模式来进行分解,这个 XML 模式定义了一个消息的内容模型。消息中封装了一个 FpML 交易。这个 XML 模式由 23 个文档组成,总大小大约为 871KB。我们将 12 个最常用的项映射到一个表的 12 个关系列中。在这种情况下,当只映射到少量的关系列时,带标注的 XML 模式分解与 XMLTABLE 分解之间的性能差别可以忽略不计。

复杂的映射

我们还使用客户提供的专用的带标注的 XML 模式做了一些测试。XML 模式定义在一个大约 142 KB 的文档中。我们将一个 XML 实例文档分解到 21 个表中分布的数百个关系列。有些元素会产生多个行插入,而有些元素只产生一个行。在这个例子中,带标注的 XML 模式分解比 XMLTABLE 分解要快上大约 40%。

一些考虑

还记得吗,对于要填充的每个表,都必须调用 XMLTABLE。如果表的数量比较多,那么可能导致成本较高。而且,每次调用 XMLTABLE 都需要遍历一次文档,但是带标注的 XML 模式分解只需遍历一次 XML 文档。使用存储过程来执行 XMLTABLE 分解会有所帮助,因为即使需要多次调用 XMLTABLE ,XML 值也只需解析一次,然后就可以在存储过程调用的生命周期中的每次遍历中重复使用。

最后,这里给出的并非正式的性能结果。建议在一个更具可控性的环境中,根据更严格的基准进行更深入的性能研究。在更深入的研究中,还可以根据更细粒度的级别定义阈值,以此确定 XML 模式或关系模式的简单或复杂度。建议定义以下几个可控制变量:XML 模式的大小(包括广度和深度),用于分解的表的数量,以及每个表中列的数量。





回页首


带标注的 XML 模式分解和 XMLTABLE 分解的最佳实践

本文已表明,带标注的 XML 模式分解和 XMLTABLE 分解是将 XML 文档分解到关系表的不同方法。在决定为应用程序采用哪种方法时,需要考虑几点问题。

实例文档与 XML 模式

在进行分解时,首先需要考虑的最明显的一点是,XMLTABLE 分解使用实例文档,而不要求有 XML 模式。带标注的 XML 模式分解则要求作为 XML 模式的一部分定义映射。

映射的变化

带标注的 XML 模式分解使用存储在 XSR 中的带标注的 XML 模式。如果映射发生变化,则必须更改 XML 模式,并重新将其注册到 XSR 中。通过 Data Studio 很容易完成这项工作。

XMLTABLE 分解使用 XQuery 表达式创建映射。当映射发生变化时,需要手动更改所有的 XQuery 表达式。如果将映射定义在一个存储过程中,就像上面的例子那样,那么当映射发生变化时,需要删除并重新创建存储过程。

映射定义的灵活性

带标注的 XML 模式分解的映射是由标注定义的。由于是 XML 模式文档的一部分,映射必须遵从 XML 模式语言的限制。如果 XML 模式语言不允许某种特定的结构,那么这种关系就不能使用带标注的 XML 模式进行映射。对于大量受支持的映射,仍然可以通过工具自动创建映射标注。而且,很容易将复杂的 XML 模式映射到多个表。

使用 XMLTABLE 分解时,INSERT 语句中用于填充列的数据可以来自不同的来源,而不仅仅限于一个 XMLTABLE 调用。而且,强大的 XQuery 使开发人员可以创建高级的表达式,从而可以更灵活地定义映射。也许惟一的限制就是开发人员的想象力了。与此同时,这种灵活性也增添了一定复杂度,如果开发人员不熟悉 XQuery 语言,那么就需要经历一段学习曲线。最后,如果映射中涉及很多表和列,那么 XMLTABLE 语句会变得十分冗长。





回页首


结束语

分享这篇文章……

digg 提交到 Digg
del.icio.us 发布到 del.icio.us
Slashdot Slashdot 一下!

最后,带标注的 XML 模式分解和 XMLTABLE 分解都是非常强大的工具,使用哪种方法取决于用户偏好。但是在某些特定的情况,使用其中的一种方法要好于使用另一种方法。

当模式比较复杂,并且需要映射到多个表时,应使用带标注的 XML 模式分解。考虑到现有的一些工具和性能因素,这是一个较好的选择。带标注的 XML 模式分解还支持一些高级特性,例如基于内容的条件分解或指定插入前应用的内容转换,本文对此不予讨论。虽然 XMLTABLE 分解也支持这一功能,但是 XML 模式中的标注可以使这项工作更容易完成。

如果没有 XML 模式,或者需要一定的灵活性,那么应该使用 XMLTABLE 分解。例如,如果要执行一个多对多的多个子元素的映射(这种映射根据动态值有条件地生成行),或者需要映射递归元素,又或者想要更精确地控制如何构造 INSERT 语句,那么可以使用 XMLTABLE 分解。

本文演示了如何使用 XMLTABLE 分解将 XML 文档分解到关系表中,这是带标注的 XML 模式分解的一种替代方法。本文还给出了关于使用每种方法的一些建议和最佳实践。它们都是 DB2 中 pureXML 技术的一部分,可以帮助开发人员和管理员以最适合他们的方式管理数据。

致谢

感谢 IBM 硅谷实验室(SVL)DB2 pureXML Development 小组中为本文贡献建议和评论的人员:Seeling Cheung、Dung Nguyen、Mayank Pradhan、David Salinero 和 Cynthia Saracco。






回页首


下载

描述名字大小下载方法
创建和注册对象的脚本mail.zip3KBHTTP
创建 XBRL 示例的脚本many-to-many.zip2KBHTTP
创建 BOM 示例的脚本recursive.zip2KBHTTP
关于下载方法的信息


参考资料

学习

获得产品和技术
  • 下载 DB2 9.5 并开始使用 XML 技术。

  • 下载 IBM Data Studio,轻松、快速地构建数据库应用程序。

  • 下载 IBM 产品评估版,并获取来自 DB2®、Lotus®、Rational®、Tivoli® 和 WebSphere® 的应用程序开发工具和中间件产品。

讨论


作者简介

Salvador 从 2002 年开始在加州 San Jose 的 IBM 硅谷实验室工作,为 DB2 z/OS 数据库产品开发 J2EE 应用程序。他当前从事 IBM 数据服务器产品的运行时和工具技术,包括 DB2 pureXML。


Bert 在 2001 年加入 IBM,从事 DB2 中的 XML 项目,是早期的架构师之一。在加入 IBM 之前,Bert 为一家刚起步的公司 Propel 工作,领导分布式、容错的中间件的设计和实现,这种中间件可托管一个可伸缩的电子商务应用程序。在此之前,Bert 曾在 Tandem Computers 从事多年 NonStop SQL 方面的工作,金融行业的很多关键应用程序都运行在这种数据库上。




对本文的评价

太差! (1)
需提高 (2)
一般;尚可 (3)
好文章 (4)
真棒!(5)

建议?







回页首


IBM 公司保留在 developerWorks 网站上发表的内容的著作权。未经IBM公司或原始作者的书面明确许可,请勿转载。如果您希望转载,请通过 提交转载请求表单 联系我们的编辑团队。
    关于 IBM 隐私条约 联系 IBM 使用条款