DIV CSS 佈局教程網

 DIV+CSS佈局教程網 >> 網頁腳本 >> XML學習教程 >> XML詳解 >> 使用 DB2 pureXML 分解 XML 文檔
使用 DB2 pureXML 分解 XML 文檔
編輯:XML詳解     

從 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 存儲過程的詳細討論超出了本文的范圍。

  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 增加了更多的語義,從而實現了 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_TotalPropertyPlantEquipment 和 BasicCalculation.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 表中分解後的數據

ARCID ARCFROM ARCTO 1 BasicCalculation.xsd#ci_TotalPropertyPlantEquipment BasicCalculation.xsd#ci_Land 2 BasicCalculation.xsd#ci_TotalPropertyPlantEquipment BasicCalculation.xsd#ci_Building 3 BasicCalculation.xsd#ci_TotalPropertyPlantEquipment BasicCalculation.xsd#ci_FurnitureFixtures 4 BasicCalculation.xsd#ci_TotalPropertyPlantEquipment BasicCalculation.xsd#ci_ComputerEquipment 5 BasicCalculation.xsd#ci_TotalPropertyPlantEquipment BasicCalculation.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 表中分解後的數據

ITEMID ITEMNAME PART PARENT 1 computersystem computer computersystem 2 computersystem motherboard computer 3 computersystem CPU motherboard 4 computersystem register CPU 5 computersystem memory motherboard 6 computersystem transistor memory 7 computersystem diskdrive computer 8 computersystem spindlemotor diskdrive 9 computersystem powersupply computer 10 computersystem powercord powersupply 11 computersystem monitor computersystem 12 computersystem cathoderaytube monitor 13 computersystem keyboard computersystem 14 computersystem keycaps keyboard 15 computersystem mouse computersystem 16 computersystem mouseball mouse

  通過查看 PARENT 列,很容易明白各部件之間的層次。例如,第 4 行是 “register” 部件。查看其父元素,可以得到 “CPU”。“CPU” 的父元素是 “motherboard” 。“motherboard” 的父元素是 “computer”。“computer” 的父元素是 “computersystem”,後者是頂級項。

  性能

  雖然沒有進行過仔細的性能研究,不過非正式的研究表明,帶標注的 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 語句會變得十分冗長。

  結束語

  最後,帶標注的 XML 模式分解和 XMLTABLE 分解都是非常強大的工具,使用哪種方法取決於用戶偏好。但是在某些特定的情況,使用其中的一種方法要好於使用另一種方法。

  當模式比較復雜,並且需要映射到多個表時,應使用帶標注的 XML 模式分解。考慮到現有的一些工具和性能因素,這是一個較好的選擇。帶標注的 XML 模式分解還支持一些高級特性,例如基於內容的條件分解或指定插入前應用的內容轉換,本文對此不予討論。雖然 XMLTABLE 分解也支持這一功能,但是 XML 模式中的標注可以使這項工作更容易完成。

  如果沒有 XML 模式,或者需要一定的靈活性,那麼應該使用 XMLTABLE 分解。例如,如果要執行一個多對多的多個子元素的映射(這種映射根據動態值有條件地生成行),或者需要映射遞歸元素,又或者想要更精確地控制如何構造 INSERT 語句,那麼可以使用 XMLTABLE 分解。

  本文演示了如何使用 XMLTABLE 分解將 XML 文檔分解到關系表中,這是帶標注的 XML 模式分解的一種替代方法。本文還給出了關於使用每種方法的一些建議和最佳實踐。它們都是 DB2 中 pureXML 技術的一部分,可以幫助開發人員和管理員以最適合他們的方式管理數據。

  下載

描述 名字 大小 下載方法 創建和注冊對象的腳本 mail.zip 3KB HTTP 創建 XBRL 示例的腳本 many-to-many.zip 2KB HTTP 創建 BOM 示例的腳本 recursive.zip 2KB HTTP


XML學習教程| jQuery入門知識| AJAX入門| Dreamweaver教程| Fireworks入門知識| SEO技巧| SEO優化集錦|
Copyright © DIV+CSS佈局教程網 All Rights Reserved