簡介
DB2 中的 pureXML 支持為管理 XML 數據提供了高效且通用的功能。DB2 以 XML 數據自身固有的分層格式存儲和處理這些數據,避免因為將 XML 存儲為 CLOB 中的文本或將它映射為關系表而導致的性能和靈活性限制。與僅使用 XML 的數據庫不同,DB2 V9 還提供了關系型數據與 XML 數據在數據庫中的無縫集成 —— 甚至是表的某一行中的集成。這樣的靈活性表現在語言支持中,使您可訪問關系型數據、XML 數據,或者同時訪問這兩種數據。您可以通過以下四種可選方案中的任一種查詢 XML:
普通 SQL(不包含 XQuery)
SQL/XML,即嵌入了 XQuery 的 SQL
XQuery 作為獨立語言(不包含 SQL)
嵌入了 SQL 的 XQuery
關於使用 XQuery 和 SQL/XML 查詢 XML 數據的介紹,請參閱 developerWorks 中的 前幾期文章,“用 SQL 查詢 DB2 XML 數據” 及 “使用 XQuery 查詢 DB2 XML 數據”。本文假設您已經熟悉這兩篇文章中介紹的基本概念。請注意,XPath 是 XQuery 的一種子語言,因此我們提到 XQuery 時,也暗中包含 XPath 語言。如果您使用過 DB2 XML Extender 中的 XSLT 樣式表或位置路徑,那麼您應該已經了解 XPath。在很多時候,使用 XPath 足以提取 XML 值或表示 XML 謂詞,因此,即便您還不熟悉 XQuery 的所有其他特性,也可以開始使用 XPath。
DB2 使您能夠利用所有這些可選方案最大限度地提高生產力、使查詢適應應用程序的需求。本文將說明的問題如下:
這四種可選方案的關鍵特征是什麼?各有哪些優缺點?
您應該在哪種情況下選擇哪種方案?
讓我們先給出一個高度總結,然後再詳細研究各個可選方案的細節和特定實例。
總結與指導原則
您可以在普通 XQuery、SQL/XML 或具有內置 SQL 的 XQuery 中表達許多查詢。在特定情況下,您可能會發現其中之一能夠比其他方案更為直觀地表達您的查詢邏輯。一般而言,查詢 XML 的 “正確” 途徑需要在 “逐個處理” 的基礎上加以選擇,需要考慮應用程序的具體需求和特征。但我們可總結出以下指導原則。
不帶任何 XQuery 或 XPath 的普通 SQL 僅對全文檔檢索以及整個文檔的插入、刪除、更新操作有用。文檔的選擇必須基於同一表中的非 XML 列。
帶有嵌入在 SQL 內的 XQuery 或 XPath 語句的 SQL/XML 提供了最廣泛的功能性和最少的局限性。您可在 XML 列上表示謂詞、提取文檔片段、向 XQuery 表達式傳遞參數標記、使用全文本搜索、SQL 級聚集與分組,您還可以用一種靈活的方式將關系型數據與 XML 數據進行聯合和連接。這種方案可很好地服務於絕大多數應用程序。即便您不是立即需要利用所有這些優勢,可能仍然會考慮選擇這種方案,從而使您的選擇能夠應對未來的擴展。
XQuery 是一種強大的查詢語言,專為查詢 XML 數據而設計。同樣,如果您的應用程序只需查詢和操縱 XML 數據,且不涉及任何關系型數據,那麼 XQuery 也是一種極為出色的選擇方案。此方案有時可能較為簡單直觀。此外,如果您正從一個僅使用 XML 的數據庫移植到 DB2 9,且已有 XQuery,那麼您很可能願意繼續使用 XQuery。
嵌入了 SQL 的 XQuery 在您希望利用關系型謂詞和索引同時又想利用全文本搜索預先過濾隨後將作為 XQuery 輸入的 XML 列中的文檔時不失為明智之選。嵌入在 XQuery 中的 SQL 允許您在 XML 列上運行外部函數。但若您需要執行帶有分組和聚集的數據分析查詢,那麼 SQL/XML 是更好的選擇。
無論您選擇在一條語句中怎樣結合 SQL 和 XQuery,DB2 都使用一種混合編譯器來為整個查詢生成及優化一種執行規劃 —— 不會導致查詢執行的性能損失。
下表總結了查詢 XML 數據的四種不同可選方案的各自優點。
表 1. 總結 普通 SQL SQL/XML 普通 XQuery 嵌入了 SQL/XML 的 XQuery XML 謂詞 - ++ ++ ++ 關系謂詞 ++ ++ - + XML 及關系謂詞 - ++ - ++ 將 XML 與關系型相連接 - ++ - ++ 將 XML 與 XML 相連接 - + ++ ++ 轉換 XML 數據 - o ++ ++ 插入、更新和刪除 ++ ++ - - 參數標記 + ++ - - 全文本搜索 + ++ - ++ XML 聚集與分組 - ++ o o 函數調用 ++ ++ - ++在上表中,“-” 表示給定語言不支持某項特性;“+” 表示支持此特性,但存在更有效或更便捷的方式;“++” 表示給定語言極為適合表現該特性;最後,“o” 表示盡管可表現此特性,但從某種程度上來說,效果非常糟糕或者效率很低。
現在,讓我們來定義一些示例數據和表,以查看具體的查詢示例。
示例表和數據
在討論 XML 查詢可選方案的過程中,我們使用了以下三個表。dept 表有兩列,列名分別為 unitID 和 deptdoc。dept 表的每一個行都描述了一家虛構企業的一個部門。unitID 列標識包含部門的單位(一個單位可能包含多個部門),deptdoc 列包含一個列出部門中的員工的 XML 文檔。project 表只有一列,列名為 projectDoc,類型為 XML。projectDoc 表中的每一個行都包含一個描述特定項目的 XML 文檔。一個項目可能會涉及到多個部門。為闡明混合關系型查詢、XML 查詢和連接,我們還提供了一個純粹的關系型表 —— unit,其中列出了各單位的名稱、主管等信息。一個單位可包含多個部門。
create table dept( unitID char(8), deptdoc XML)
unitID
deptdoc
WWPR
<dept deptID="PR27">
<employee id="901">
<name>Jim Qu</name>
<phone>408 555 1212</phone>
</employee>
<employee id="902">
<name>Peter Pan</name>
<office>216</Office>
</employee>
</dept>
WWPR
<dept deptID="V15">
<employee id="673">
<name>Matt Foreman</name>
<phone>416 891 7301</phone>
<poffice>216</Office>
</employee>
<description>This dept supports sales world wide</description>
</dept>
S-USE
...
...
...
create table project(projectDoc XML)
projectDOC
<project ID="P0001">
<name>Hello World</name>
<deptID>PR27</deptID>
<manager>Peter Pan</manager>
</project>
<project ID="P0009">
<name>New Horizon</name>
<deptID>PR27</deptID>
<deptID>V15</deptID>
<manager>Matt Foreman</manager>
<description>This project is brand new</description>
</project>
create table unit( unitID char(8) primary key not null, name char(20), manager char(20),…)
unitID
名稱
主管
...
WWPR
Worldwide Marketing
Jim Qu
...
S-USE
Sales US East Coast
Tom Jones
...
...
...
...
...
普通 SQL
您可使用不帶任何 XPath 或 XQuery 的普通 SQL,在不使用 XML 謂詞的情況下讀取整個文檔。只要應用程序能識別出 XML 文檔,從而基於同一表中的關系謂詞進行全文檔檢索,那麼這種方法就是一種非常簡單易行的方法。例如,查詢 1 檢索 “WWPR” 這個單位的所有 department 文檔:
查詢 1
select deptdoc
from dept
where unitID = 'WWPR';
同樣,查詢 2 返回 Jim Qu 所管理的所有部門的 XML 數據。
查詢 2
select deptdoc
from dept d, unit u
where d.unitID = u.unitID and u.manager= 'Jim Qu';
顯而易見的缺陷就是您無法表示關於 XML 數據本身的謂詞,也無法僅檢索 XML 文檔的片段。在我們的示例中,普通 SQL 不足以實現僅選擇部門 PR27 且僅返回員工姓名這項任務。
若您的查詢不需要在 XML 上應用謂詞,並且總是返回整個 XML 文檔,那麼普通 SQL 足以滿足您的需要。在本例中,將在 VARCHAR 或 CLOB 列中存儲 XML,對於全文檔插入及檢索操作而言,這將給您帶來一定的性能優勢。
普通 SQL 沒有使用 DB2 V9 支持的任何 SQL/XML 或 XQuery,但它依然允許在查詢中使用全文本搜索條件。通過 DB2 Net Search Extender,您可以在 XML 列上創建全文本索引來支持文本搜索 —— 從基本的關鍵字搜索到高級的 37 種語言的詞干提取(stemming)、辭典(thesaurus)和模糊搜索。您還可以通過 path 表達式將文本搜索限定為僅搜索特定文檔部分。以文本-索引查找為基礎,查詢 3 返回 /dept/description 目錄下所有包含字符串 “sales” 的 department 文檔:
查詢 3
select deptdoc
from dept
where CONTAINS(deptdoc,'SECTION("/dept/description") "sales" ')=1;
SQL/XML(嵌入 SQL 中的 XQuery/XPath)
SQL/XML 是 SQL 語言標准的一部分,定義了一種新的 XML 數據類型以及多種查詢、構造、驗證和轉換 XML 數據的函數。DB2 V8 已加入了許多 SQL/XML 發布函數,使用戶能夠使用 XMLELEMENT、XMLATTRIBUTE、XMLFOREST、XMLAGG 和其他一些函數將關系型數據構造為 XML。
DB2 9 添加了新的 SQL/XML 查詢函數,包括 XMLQUERY、XMLTABLE 函數以及 XMLEXISTS 謂詞。這些函數允許用戶在 SQL 語句中嵌入 XQuery 或簡單的 XPath 表達式。
如查詢 4 所示,XMLQUERY 函數通常用在 select 子句中,用於從 XML 列中提取 XML 片段,而 XMLEXISTS 通常在 where 子句中使用,用於在 XML 數據上應用謂詞。
查詢 4
select unitID, XMLQUERY('for $e in $d/dept/employee return $e/name/text()'
passing d.deptdoc as "d")
from dept d
where unitID LIKE 'WW%' and
XMLEXISTS('$d/dept[@deptID = "V15"]' passing d.deptdoc as "d");
這個示例查詢使用 XMLEXISTS 來選擇 “WW” 部門 V15,並應用 XMLQUERY 來返回該 department 文檔中的所有員工姓名。查詢結果如下:
WWPR Matt Foreman此查詢還突出展示了以一種整合的方式使用 SQL/XML 查詢 XML 數據和關系型數據的方法。select 子句從關系型列和 XML 列中檢索數據,而 where 子句包含關系型謂詞和 XML 謂詞。我們可使用 XMLTABLE 函數表示同一查詢,如查詢 5 所示。在這種格式的查詢中,我們指定了條件,以限制輸入數據並提取那些我們感興趣的輸出值。在查詢 5 中,XMLTABLE 函數中的 XQuery 表達式指定了在部門 V15 中工作的員工,而 COLUMNS 子句中的 path 表達式(“name/text()”)則返回其名稱。此查詢的輸出結果與查詢 4 相同。
查詢 5
select d.unitID, T.name
from dept d, XMLTABLE('for $emp in $d/dept[@deptID=”V15”]/employee
return $emp'
passing d.deptdoc as “d”
COLUMNS
name varchar(50) path 'name/text()' ) as T
where unitID LIKE 'WW%';
SQL/XML 的優點
SQL/XML 方案具有以下優點:
如果您有一個現有 SQL 應用程序,需要在各處逐步添加一些 XML 功能,那麼 SQL/XML 具有一定的優勢。
如果您是 SQL 的忠實愛好者,並且希望以 SQL 為首選語言 —— 因為它是您和您的團隊最熟悉的語言,那麼 SQL/XML 有優勢。
如果您的查詢需要同時從關系型列和 XML 列中返回數據,那麼 SQL/XML 有優勢。
如果您的查詢需要如查詢 3 所示的全文本搜索條件,那麼 SQL/XML 有優勢。
如果您希望將結果作為集合返回,且將丟失的 XML 元素表示為空,那麼 SQL/XML 有優勢。
如果您希望使用參數標記,那麼 SQL/XML 有優勢,因為 DB2 V9 XQuery 不支持外部參數。XMLQUERY、XMLTABLE 和 XMLEXISTS 中的傳遞機制允許您將一個 SQL 參數標記作為變量($x)傳遞到嵌入式 XQuery 表達式中:
查詢 6
select deptdoc
from dept
where XMLEXISTS('$d/dept[@deptID = $x]'
passing deptdoc as "d", cast(? as char(8)) as "x");
對於那些需要整合關系型數據和 XML 數據的應用程序來說,SQL/XML 有優勢。它提供了連接 XML 數據與關系型數據的最簡便途徑。以下示例選擇擁有 unit 表中作為主管列出的那些員工的所有部門的 unit ID。此查詢執行了一個關系型值(unit.manager)和 XML 值(//employee/name)之間的連接:
查詢 7
select u.unitID
from dept d, unit u
where XMLEXISTS('$d//employee[name = $m]'
passing d.deptdoc as "d", u.manager as "m");
為完成此連接,我們將 unit manger 傳遞到 XMLEXISTS 謂詞之中,這樣實際連接條件就是一個 XQuery 謂詞。反之,我們可將 XML 文檔中的員工姓名提取出來,傳遞到 SQL 上下文中,從而使連接條件成為一個 SQL 謂詞:
查詢 8
select u.unitID
from dept d, unit u
where u.manager = XMLCAST(XMLQUERY('$d//employee/name '
passing d.deptdoc as "d") as char(20));
通常,查詢 7 要優於查詢 8,因為 XMLCAST 函數只需要一個輸入值。但在我們的示例中,對於那些有著多名符合條件的員工的部門,此查詢將失敗。查詢 8 使用了 XMLCAST,對於各文檔只需進行一次 XML 值連接非常有用,因為它允許在 unit.manager 上使用關系型索引。此索引無法在查詢 7 中使用,因為其連接條件並非關系型謂詞,而是一個 XQuery 謂詞。
SQL/XML 在 XML 分組和聚集方面有優勢。XQuery 語言不提供顯式的 group-by 結構。盡管可利用自連接在 XQuery 中表示分組和聚集,但那非常糟糕。作為示例,讓我們計算一下按辦公室分組的員工數量,換句話說,就是各辦公室中的員工數量。查詢 9 展示了普通 XQuery 中的實現方式。查詢 9 中的 db2-fn:xmlcolumn 函數提供了 DB2 中的 XML 數據訪問。它接受作為參數傳入的 XML 列名,然後返回存儲在該列中的 XML 值序列。使用 SQL/XML 函數(如 XMLTABLE 或 XMLQUERY)更簡單,它們可使您輕松從 XML 列中提取數據項,然後使用熟悉的 SQL 概念在此基礎上表示分組和聚集。查詢 10 返回與查詢 9 相同的邏輯結果,但使用的是 SQL 的 group by 子句。
查詢 9
XQUERY
for $o in distinct-values(db2-fn:XMLcolumn("DEPT.DEPTDOC")/dept/employee/Office)
let $emps := db2-fn:XMLcolumn("DEPT.DEPTDOC")/dept/employee[Office/text()=$o]
return
<result><office>{$o}</Office><cnt>{count($emps)}</cnt></result>;
Result:
<result><office>216</Office><cnt>2</cnt></result>
查詢 10
select X.Office, count(X.emp)
from dept, XMLTABLE ('$d/dept/employee' passing deptdoc as "d"
COLUMNS
emp VARCHAR(30) PATH ' name',
office INTEGER PATH ' Office ') as X
GROUP BY X.Office;
Result:
216 2
- 1
在查詢 10 中,XMLTABLE 函數從帶有 “emp” 和 “office” 這兩列的表形式的所有文檔中提取 /dept/employee/name 和 /dept/employee/Office。對該表使用 SQL 的 group by 函數和聚集函數通常比使用生成相同結果的普通 XQuery 更有效。
請注意,在查詢 10 中多獲得了一行,這是因為 SQL 的 group by 函數還為 NULL 值生成一個組,我們的示例表中有一名員工沒有辦公室信息。查詢 9 沒有為該員工生成一行,因為 for 循環遍歷的是不同的 Office 值,這些值不包括丟失的辦公室信息。
SQL/XML 的缺點
對於將 XML 文檔轉換成另一個 XML 文檔,SQL/XML 並非總是最佳選擇。在僅需處理 XML 數據時,使用獨立通常更合適,也更直觀。
在 SQL/XML 中表示兩個 XML 列 —— 或者更常見的情況,兩個 XML 值之間的連接非常不便,用普通 XQuery 通常更高效、更直觀。例如,查詢 11 連接 dept 和 project 表的 XML 列,返回執行任一項目的員工。
查詢 11
select XMLQUERY('$d/dept/employee' passing d.deptdoc as "d")
from dept d, project p
where XMLEXISTS('$d/dept[@deptID=$p/project/deptID] '
passing d.deptdoc as "d", p.projectDoc as "p");
在查詢 11 中,我們將每一個 dept 和 project 文檔放入 XMLEXISTS 中傳遞給 XQuery,並在那裡表示連接條件。您可能會發現,在普通 XQuery 中(查詢 13)編寫此類連接更為簡單高效。
XQuery 作為獨立語言
讓我們暫時回過頭來,問一個問題:什麼是 XQuery,我們又為什麼需要使用 XQuery?正如 SQL 是為關系型數據模型設計的查詢語言一樣,XQuery 是專為查詢 XML 數據設計的語言。由於 XML 數據可能與關系型數據有很大的差異,因此我們需要一種專門的語言來高效處理 XML 數據。關系型數據是平面的、高度結構化、強類型化、無序的,而 XML 數據是有序、嵌套的、層次化的、可選類型化的,並且往往不規則且是不完全結構化的。SQL 無法處理這樣的情況,但 XQuery 專門為此設計。具體而言,XQuery 設計用於遍歷 XML 文檔樹,並提取 XML 片段,而且還包括一些表達式,這些表達式用於創建、操縱和遍歷 XML 項序列及構建新的 XML 數據。
IBM 已擴展了 DB2 的所有主要應用程序編程接口(API),以支持將 XQuery 作為 SQL 那樣的一流語言。其中包括 CLI/ODBC、嵌入式 SQL、JDBC 和 .Net。因此,DB2 命令行處理器也支持 XQuery。您可按原樣提交 XQuery,但需要使用 XQUERY 關鍵字啟動它們,以通知 DB2 使用 XQuery 解析器,如下例所示:
查詢 12
XQUERY
for $dept in db2-fn:XMLcolumn("DEPT.DEPTDOC")/dept
where $dept/@deptID="PR27"
return $dept/employee/name;
查詢 12 遍歷各 department 文檔中的 “dept” 元素,並返回在部門 PR27 工作的員工姓名。
XQuery 的優點
對於僅涉及 XML、不需要(或不希望)使用 SQL 或關系結構的應用程序來說,XQuery 很適用。
對於從僅包含 XML 的數據庫到 DB2 V9 的移植來說,XQuery 很適用。現有 XQuery 很可能僅需做出小小的修改即可在 DB2 中運行。例如,DB2 中的 XQuery 的數據輸入來自 db2-fn:XMLcolumn() 函數,而其他數據庫可能稱之為 collection()。在這種情況下,僅需重新對函數進行命名即可。
如果查詢結果需要嵌入在新構造的 XML 文檔(此文檔與數據庫中其他 XML 文檔不同)中(並以這個新文檔的形式返回),那麼 XQuery 有優勢。
XQuery 非常適合表示兩個 XML 文檔之間的連接以及聯合 XML 值。例如,使用 XQuery,您可以將查詢 11 中的連接以一種更為直接、高效的方式表示出來,如查詢 13 所示。
查詢 13
XQUERY
for $dept in db2-fn:XMLcolumn("DEPT.DEPTDOC")/dept
for $proj in db2-fn:XMLcolumn("PROJECT.PROJECTDOC")/project
where $dept/@deptID = $proj/deptID
return $dept/employee;
XQuery 的缺點
使用普通 XQuery,您無法利用 DB2 Net Search Extender(NSE)所提供的全文本搜索功能。要進行全文本搜索,則需要包含 SQL。
普通 XQuery 不允許您調用 SQL 用戶定義的函數(UDF)或者用 C 語言或 Java 編寫的外部 UDF。
目前,DB2 不提供調用帶參數標記的獨立 XQuery 的方法。要向 XQuery 傳遞參數,則必須使用 SQL/XML 將一個參數標記(“?”)轉換為指定變量。例如,在查詢 12 中,您可能希望使用一個問號(?)作為 SQL 風格的參數標記,來取代字面值 “PR27”。但那是無效查詢。
在查詢 6 中,可以看到 SQL/XML 允許您將一個 SQL 參數標記作為變量傳遞到嵌入式 XQuery 表達式中。SQL/XML 查詢的 select 子句中通常有一個 XMLQUERY 函數,用於提取 XML 文檔的某些部分;where 子句中通常會有一個 XMLEXISTS 謂詞,用於過濾滿足條件的文檔。如果您希望在一條 FLWOR 表達式中表示整個查詢邏輯,而不是使用兩個單獨的 XQuery 調用(XMLQUERY 中的一個調用和 XMLEXISTS 中的一個調用),並且希望依然使用參數標記,那麼可以考慮將查詢 12 重寫為查詢 14 所示形式:
查詢 14
values( XMLQUERY(
' for $dept in db2-fn:XMLcolumn("DEPT.DEPTDOC")/dept
where $dept/@deptID = $z
return $dept/employee/name'
passing cast(? as char(8)) as "z" ) );
查詢 14 是一個 SQL/XML 語句,因為它是 SQL fullselect 的 values 子句。values 子句通過指定結果表中的各列表達式來返回一個值表。在查詢 14 中,結果表為 XML 類型的單行單列表,XMLQUERY 函數生成輸出列的值。所有員工名都將在一個 XML 值中返回給客戶機。查詢 14 中的 FLOWR 表達式幾乎與查詢 12 中的表達式相同,惟一的不同在於查詢 14 包含一個外部變量($z),此變量被作為參數傳遞到 XMLQUERY 函數中。
嵌入了 SQL 的 XQuery
普通 XQuery 允許且只允許訪問 XML 數據。如果只處理 XML 數據,那麼這種方案非常好,但如果應用程序需要利用兩種語言和數據模型的全部力量,綜合訪問 XML 數據和關系型數據,那麼普通 XQuery 不足以勝任。使用本文前面討論過的 SQL/XML —— 嵌入了 XQuery 的 SQL —— 可完成此任務。相反地,嵌入了 SQL 的 XQuery 為您帶來了額外的可能性。除前文列出的優缺點之外,還有以下幾個重要的方面:
嵌入了 SQL 的 XQuery 的優點
如果希望以關系列上的條件為依據,僅處理 XML 文檔的子集,那麼嵌入了 SQL 的 XQuery 非常適用。特別是您可以使用關系型謂詞來約束特定 XQuery 的輸入。為此,DB2 提供了另外一個輸入函數 db2-fn:sqlquery,以便在 XQuery 中調用 SQL 查詢。此函數接受 SQL SELECT 語句,並返回 XML 列作為輸出。例如,查詢 15 不考慮 XML 列的 deptdoc 中的所有文檔,它有一個嵌入式 SQL 語句,通過連接到應用了謂詞的 unit 表來預先過濾 XML 文檔:
查詢 15
XQUERY
for $emp in db2-fn:sqlquery("select deptdoc
from dept d, unit u
where d.unitID=u.unitID and
u.manager = 'Jim Qu'")/dept/employee
where $emp/Office = 216
return $emp/name;
兩個表的 unitID 列上的常規關系索引以及 unit 表的 manager 列上的常規關系索引有助於提高嵌入式 SQL 查詢的速度。
嵌入了 SQL 的 XQuery 允許您利用全文本搜索,您可在嵌入式 SQL 語句的 where 子句中使用文本搜索函數 “contains”。在查詢 16 中,XQuery 中的 SQL 從 dept 表中選擇文檔,其中文字 “sales” 出現在 /dept/description 中的某處。全文本索引迅速找到這些 XML 文檔,隨後將它們輸入到從這些文檔中提取所有員工姓名的 FLWOR 表達式中。就我們的示例表而言,查詢 17 返回相同的結果,但是使用 SQL/XML 表示法表示的。
查詢 16
XQUERY
for $emp in db2-fn:sqlquery("
select deptdoc from dept
where CONTAINS(deptdoc, 'SECTION(""/dept/description"") ""sales"" ')=1
")//employee
return $emp/name;
查詢 17
select XMLQUERY('$d//employee/name' passing deptdoc as "d")
from dept
where CONTAINS(deptdoc,'SECTION("/dept/description") "sales" ')=1;
嵌入了 SQL 的 XQuery 對於那些需要整合關系型數據和 XML 數據的應用程序非常有用。您可以通過組合方式查詢 XML 數據與關系型數據。這同樣適用於 SQL/XML。但您可能會發現,在 SQL/XML 中利用 XMLEXISTS 函數連接 XML 值和關系值更輕松。請比較查詢 18 和查詢 19。查詢 18 返回其員工中包含單位主管的那些部門的 deptID。嵌入式 SQL 語句將 unit 表中的主管姓名強制轉換為 XML 類型(本例中為 XML 字符串),然後將其提供給 XQuery。XQuery 的 where 子句包含連接條件,這些條件用於主管姓名與員工姓名元素的比較。查詢 19 用 SQL/XML 表示法表示了同樣的連接。
查詢 18
XQUERY
for $m in db2-fn:sqlquery('select XMLCAST(u.manager as XML) from unit u')
for $d in db2-fn:XMLcolumn("DEPT.DEPTDOC")/dept
where $d/employee/name = $m
return $d/data(@deptID);
查詢 19
select XMLQUERY('$d/dept/data(@deptID)' passing d.deptdoc as "d")
from dept d, unit u
where XMLEXISTS('$d//employee[name = $m]'
passing d.deptdoc as "d", u.manager as "m");
從查詢 18 中可以看出,嵌入了 SQL 的 XQuery 對於將關系型數據提供給 XQuery 來說非常有用。這允許您聯合及合並 XML 數據和關系型數據。查詢 20 構造了一個 result 文檔,其中包含單位和部門信息。部門信息是從 XML 列的 deptdoc 中檢索的 XML 文檔。單位信息來自關系型表 unit。嵌入式 SQL 語句使用 SQL/XML 發布函數來構造 XML 元素 “Unit”,該元素帶有三個子元素,子元素的值來自 unit 表的關系列,即 unitID、name 和 manager 這三列。
查詢 20
XQUERY
let $d := db2-fn:sqlquery("select deptdoc from dept where unitID = 'WWPR' ")
let $u := db2-fn:sqlquery("select XMLELEMENT(NAME ""Unit"",
XMLFOREST(unitID, name, manager))
from unit where unitID = 'WWPR' " )
return <result>
<units>{$u}</units>
<department>{$d}</department>
</result>;
查詢 20 的輸出結果形式如下所示:
<result>
<units><unit>
<UNITID> WWPR </UNITID>
<NAME> World Wide Markeing</NAME>
<MANAGER> Jim Qu </MANAGER>
</unit>
<unit>
<UNITID> WWPR </UNITID>
<NAME> ... </NAME>
<MANAGER> ... </MANAGER>
</unit>
....
</units>
<department>
<dept deptID="PR27">
<employee id="901">
<name>Jim Qu</name>
<phone>408 555 1212</phone>
</employee>
<employee id="902">
<name>Peter Pan</name>
<office>216</Office>
</employee>
</dept>
</department>
</result>
嵌入了 SQL 的 XQuery 比較出色,因為嵌入式 SQL 語句可包含對用戶定義的函數(UDF)的調用。UDF 在許多 IT 組織內都得到廣泛應用,用於封裝關鍵業務邏輯和簡化應用程序開發需求。這非常重要,因為普通 XQuery 不能調用 UDF。
嵌入了 SQL 的 XQuery 的缺點
盡管 db2-fn:sqlquery 允許在 XQuery 內嵌入 SQL 語句,但目前還不能在嵌入式 SQL 語句中使用參數標記。
目前,db2-fn:sqlquery 不允許從 XQuery 向 SQL 中傳遞參數。因此在結合 XML 數據和關系型數據方面,SQL/XML 更強大。例如,嵌入了 SQL 的 XQuery 無法像 SQL/XML 那樣用於表示關系列和 XML 列之間的連接。
XML 查詢結果
您需要注意的一件事就是:根據編寫具體查詢的方式不同,DB2 可能會交付不同格式的查詢結果。例如,普通 XQuery 返回結果集形式的項(例如元素或文檔片段),一項作為一行,即便多個項來自數據庫中的同一文檔(行)也是如此。另一方面,在使用 XMLQUERY 函數時,SQL/XML 可能會在一行中返回多個項,而不是返回多個獨立的行。在某些情況下,您會認為這很令人滿意,但在某些情況下又並非如此,具體取決於您的特定應用程序。讓我們來看看示例。
查詢 21 和查詢 22 均請求獲得 dept 文檔中的員工姓名。查詢 21 以 SQL/XML 方式表示,而查詢 22 是用 XQuery 編寫的。
查詢 21
select XMLQUERY('$d/dept/employee/name' passing deptdoc as "d")
from dept;
查詢 22
XQUERY db2-fn:XMLcolumn("DEPT.DEPTDOC")/dept/employee/name;
查詢 21 為每個 dept 文檔都返回一行,每個行都包含在該部門中工作的員工姓名:
<name>Jim Qu </name> <name>Peter Pan </name> <name>Matt Foreman</name>而查詢 22 將每一個員工姓名作為獨立的一行返回:
<name>Jim Qu </name> <name>Peter Pan </name> <name>Matt Foreman</name>查詢 22 的結果通常更易於為應用程序所用,即:一次處理一個 XML 值。但在這個示例中,您並不知道哪些 name 元素來自同一個 department 文檔。查詢 21 的輸出結果保留了這一信息,但應用程序利用其結果較為困難,因為應用程序可能需要分離這些結果行,以便分別訪問每個 name 元素。如果應用程序使用 XML 解析器從 DB2 中獲取每個 XML 結果行,那麼解析器將拒絕查詢 21 的第一個結果行,因為它並非格式良好的文檔(缺少單獨的根元素)。為解決此問題,您可向查詢 21 中添加一個 XMLELEMENT 構造函數,從而添加一個根元素,如查詢 23 所示:
查詢 23
Select XMLELEMENT(name "employees",
XMLQUERY('$d/dept/employee/name' passing d.deptdoc as "d"))
from dept d;
查詢結果發生了改變,每個結果行都是一個格式良好的 XML 文檔:
<employees><name>Jim Qu </name><name>Peter Pan </name></employees> <employees><name>Matt Foreman</name></employees> ....回憶一下,查詢 14 使用了一個 SQL values 子句和 XMLQUERY 函數來允許向 XQuery 傳遞參數。但查詢 14 的輸出結果為單行,其中包含所有員工的姓名。如果您更希望使各員工的姓名自成一行,同時還需要使用參數標記,那麼可以借助 XMLTABLE 函數,如查詢 24 所示:
查詢 24
select X.*
from dept d, XMLTABLE('for $dept in $d/dept
where $dept/@deptID = $z
return $dept/employee/name'
passing d.deptdoc as "d", cast(? as char(10)) as "z"
COLUMNS
"name" XML PATH ".") as X ;
結束語
DB2 V9 為查詢 XML 數據提供了一組豐富的選項。您將根據應用程序的需求和特征選擇最適合您的方案。如果需要結合關系型數據和 XML 數據,那麼 SQL/XML 在絕大多數情況下都是最佳選擇。具體而言,SQL/XML 方案允許您為 XML 數據使用參數標記。如果僅涉及 XML 的應用程序,那麼獨立 XQuery 是一種功能強大的方案,嵌入式 SQL 可進一步增強它,支持您使用全文本搜索和 UDF 調用。本文所討論的示例幫助您作出了明智的決策。您可使用我們的查詢模式作為起點,編寫自己的查詢,查詢您自己的 XML 數據。
一個通用的指導原則就是根據實際需要為查詢引入恰如其分的復雜性。例如,使用一個帶有內置 XQuery 的嵌入式 SQL 語句的 XQuery 是完全可能的。但是,我們的經驗顯示,表達所需的查詢邏輯通常並不需要兩種語言的嵌套層次超過一層。因此,我們建議僅在 SQL 中使用一層嵌入式 XQuery,反之亦然。