級(jí)別: 初級(jí)
陳 力
(chenli44@yeah.net), 北京師范大學(xué)信息管理專業(yè)研究生
張 大為 (zhangdaw@cn.ibm.com), 軟件工程師
2006 年 9 月 21 日
摘要:本文將介紹 DB2 提供的一些基本 XML 函數(shù),并結(jié)合一個(gè)簡(jiǎn)單的實(shí)例,重點(diǎn)介紹如何利用 DB2 提供的 XML 函數(shù)以視圖或查詢的形式靈活的實(shí)現(xiàn) XML 文檔的構(gòu)造和發(fā)布。
DB2 提供了豐富的功能用以發(fā)布 XML 格式的數(shù)據(jù),這種功能包括了 DB2 內(nèi)置的 XML 函數(shù)以及 XML extender 提供的功能(包括了發(fā)布、解析、檢索、存儲(chǔ)等多種功能)。對(duì) XML 文檔的支持,使得利用現(xiàn)有的 DB2 數(shù)據(jù)庫(kù),就能夠方便的實(shí)現(xiàn)系統(tǒng)或者數(shù)據(jù)的整合。
本文讀者定位為具有一定的 DB2 開發(fā)經(jīng)驗(yàn),同時(shí)對(duì) XML 語(yǔ)言的格式、schema 定義、命名空間等有基本的理解。
本文將介紹 DB2 提供的一些基本 XML 函數(shù),并結(jié)合一個(gè)簡(jiǎn)單的實(shí)例,重點(diǎn)介紹如何利用 DB2 提供的 XML 函數(shù)以視圖或查詢的形式靈活的實(shí)現(xiàn) XML 文檔的構(gòu)造和發(fā)布。同時(shí)文中還將介紹利用作者編制的一個(gè)工具,根據(jù)目標(biāo) XML 的樣例文檔生成相應(yīng)的包含 XML 函數(shù)的查詢框架,以輔助開發(fā)較為復(fù)雜的 XML 文檔結(jié)構(gòu)的 SQL 語(yǔ)句。
以 DB2 的 XML 函數(shù)為基礎(chǔ)的 SQL 語(yǔ)句支持 XML 語(yǔ)言的絕大部分特征,可以簡(jiǎn)單而直觀地實(shí)現(xiàn)從關(guān)系型數(shù)據(jù)模型到 XML 的樹狀結(jié)構(gòu)的轉(zhuǎn)換,其靈活性接近于手工書寫 XML 文檔,同時(shí),只要利用適當(dāng)?shù)姆椒ê凸ぞ撸涂梢栽诓辉黾娱_發(fā)復(fù)雜度的基礎(chǔ)上大大降低開發(fā)的工作量。
1.DB2 XML 查詢/視圖及 XML 函數(shù)簡(jiǎn)介
利用 XML 函數(shù)我們可以方便的構(gòu)建 XML 查詢或視圖,利用這些函數(shù)發(fā)布 XML 的整個(gè)過(guò)程與開發(fā)和使用傳統(tǒng)的查詢與視圖并沒(méi)有本質(zhì)的區(qū)別。XML 查詢不需要依賴于復(fù)雜的發(fā)布程序和軟件平臺(tái),即使只有 DB2 命令行客戶端,我們也能夠連接數(shù)據(jù)庫(kù)發(fā)布 XML 文檔。可見利用這種方式進(jìn)行 XML 發(fā)布具有很廣泛的應(yīng)用范圍,能夠適應(yīng)從高級(jí)語(yǔ)言開發(fā)的網(wǎng)絡(luò)程序到簡(jiǎn)單的 Shell 腳本這樣不同級(jí)別實(shí)現(xiàn)的要求,而且特別有利于簡(jiǎn)單直接的解決方案。這給我們以 DB2 數(shù)據(jù)庫(kù)為基礎(chǔ)實(shí)現(xiàn)異構(gòu)數(shù)據(jù)系統(tǒng)的整合與互動(dòng)功能提供了一個(gè)簡(jiǎn)便易行的方法。
DB2 UDB 8.2 直接提供了 7 種 SQL/XML 函數(shù),以及與這些 XML 語(yǔ)法成分對(duì)應(yīng)的 XML 數(shù)據(jù)類型。
XML函數(shù)列表:
XMLSERIALIZE
XMLELEMENT
XMLATTRIBUTES
XMLNAMESPACES
XMLFOREST
XMLCONCAT
XMLAGG
除了 XMLSERIALIZE 之外,其他六個(gè)函數(shù)均返回 XML 數(shù)據(jù)類型,包括了元素、屬性、命名空間、節(jié)點(diǎn)集。利用這些函數(shù)提供的 XML 成分,我們可以構(gòu)造出任意指定的 XML 文檔結(jié)構(gòu)。DB2 的 XML 函數(shù)已經(jīng)提供了足夠的功能,可以認(rèn)為只要設(shè)計(jì)人員能從業(yè)務(wù)邏輯上實(shí)現(xiàn)從關(guān)系數(shù)據(jù)模型到 XML 文檔樹狀結(jié)構(gòu)的映射關(guān)系的設(shè)計(jì),開發(fā)人員就能夠利用這些基本函數(shù)直接實(shí)現(xiàn) XML 文檔的生成。
我們?cè)谶@里使用簡(jiǎn)單的示例對(duì)上述 XML 函數(shù)一一進(jìn)行介紹。我們?cè)诒镜財(cái)?shù)據(jù)庫(kù)中創(chuàng)建了一個(gè)簡(jiǎn)單的 Contact 表,并且插入了幾條數(shù)據(jù),作為示例基礎(chǔ):
SELECT cust_num, f_name, l_name, email, cnt_num FROM contact
XMLSERIALIZE:
XMLSERIALIZE 函數(shù)是一個(gè)特殊的函數(shù),并不產(chǎn)生 XML 文檔的一部分,而是用于將其他函數(shù)產(chǎn)生的 XML 數(shù)據(jù)類型轉(zhuǎn)化為其他的常見數(shù)據(jù)類型,如 CHAR、VARCHAR 和 CLOB 等。由于 XML 數(shù)據(jù)類型不能直接存儲(chǔ)到數(shù)據(jù)庫(kù)中,也不能直接輸出,通常都需要使用 XMLSERIALIZE 函數(shù)進(jìn)行轉(zhuǎn)換,后面的示例也都會(huì)使用到它。
XMLELEMENT:
XMLELEMENT 是最基礎(chǔ)的 XML 函數(shù),用于構(gòu)建 XML 元素節(jié)點(diǎn),這個(gè)函數(shù)接受元素名稱和元素內(nèi)容兩個(gè)必選參數(shù),另外還可以接受 XML 屬性和命名空間參數(shù)。元素內(nèi)容可以是 CHAR、VARCHAR、INT 等基本數(shù)據(jù)類型,也可以是 XML 元素?cái)?shù)據(jù)類型,允許 XMLELEMENT 函數(shù)的嵌套。
清單1
SELECT
XMLSERIALIZE( CONTENT
XMLELEMENT(NAME "Contact",
XMLELEMENT(NAME "Email", RTRIM(email))
)
AS VARCHAR(5000))
xml FROM contact
where cust_num = '0000000001'
|
XMLATTRIBUTES與XMLNAMESPACES:
XMLATTRIBUTES與XMLNAMESPACES函數(shù)的使用方法非常類似,當(dāng)前者作為參數(shù)傳給XMLELMENT函數(shù)后可以為該元素添加屬性,而后者則可以向元素中添加命名空間以及命名空間前綴的聲明,多個(gè)屬性/命名空間可以通過(guò)一個(gè)XMLATTRIBUTES或XMLNAMESPACES函數(shù)一次性添加。
清單2
SELECT
XMLSERIALIZE( CONTENT
XMLELEMENT(NAME "Contact",
XMLNAMESPACES(DEFAULT 'http://www.ibm.com/schemas/DummyDemo',
'http://www.w3.org/2001/XMLSchema' as "xsd"),
XMLATTRIBUTES(cust_num as "CustNo", 1 as "SeqNo"),
XMLELEMENT(NAME "Name",RTRIM(f_name) || ' ' || RTRIM(l_name))
)
AS VARCHAR(5000))
xml FROM contact FETCH FIRST 1 ROWS ONLY
|
XMLFOREST與XMLCONCAT:
XMLFOREST與XMLCONCAT的功能類似,用以生成一組并列的XML元素節(jié)點(diǎn)使之具有相同的內(nèi)部XML數(shù)據(jù)類型,如<elem1>…</elem1><elem2>…</elem2><elem3>…</elem3>這樣的形式。XMLFOREST用于根據(jù)輸入的多個(gè)一般類型值參數(shù)生成簡(jiǎn)單的文本節(jié)點(diǎn)"森林",XMLCONCAT接受的參數(shù)只能是XML數(shù)據(jù)類型,可以用于將許多復(fù)雜的元素節(jié)點(diǎn)組合在一起形成并列的節(jié)點(diǎn)結(jié)構(gòu)。
清單3 XMLFOREST與XMLCONCAT對(duì)比
SELECT
XMLSERIALIZE( CONTENT
XMLFOREST(
cust_num,
RTRIM(f_name) as "FirstName",
RTRIM(l_name) as "LastName"
)
AS VARCHAR(5000))
xml FROM contact FETCH FIRST 1 ROWS ONLY
|
SELECT
XMLSERIALIZE( CONTENT
XMLCONCAT(
XMLELEMENT(NAME "Name",
XMLATTRIBUTES(RTRIM(cust_num) as "CustNo"),
RTRIM(f_name)|| ' ' || RTRIM(l_name)
),
XMLELEMENT(NAME "Email",RTRIM(email))
)
AS VARCHAR(5000))
xml FROM contact FETCH FIRST 1 ROWS ONLY
|
這兩個(gè)函數(shù)雖然結(jié)果形式類似,但是其功能卻是不同的。XMLFOREST 只能生成簡(jiǎn)單值元素的"森林",不能靈活的設(shè)置這些值元素的屬性和命名空間聲明等,但是它語(yǔ)法非常簡(jiǎn)單,非常適合用在處理多個(gè)葉子值節(jié)點(diǎn)并列出現(xiàn)的情況。而 XMLCONCAT 能將多個(gè)復(fù)雜的 XML 元素節(jié)點(diǎn)形成并列的節(jié)點(diǎn)結(jié)構(gòu)。XMLSERIALIZE 只能接受一個(gè) XML 數(shù)據(jù)類型的參數(shù),在上述例子中 XMLCONCAT 用來(lái)將多個(gè) XML 元素形成并列節(jié)點(diǎn)結(jié)構(gòu)并且返回一個(gè)相同的 XML 數(shù)據(jù)類型傳給 XMLSERIALIZE,如果不使用 XMLCONCAT 將無(wú)法成功調(diào)用 XMLSERIALIZE。
XMLAGG:
XMLAGG 是 XML 函數(shù)中唯一的聚集函數(shù),XMLAGG 函數(shù)可以將若干條記錄中的列值按照 XML 結(jié)構(gòu)的定義聚集成為"森林"結(jié)構(gòu),合成到一條記錄中。XMLAGG 為我們根據(jù)數(shù)據(jù)實(shí)現(xiàn)動(dòng)態(tài)樹狀結(jié)構(gòu)提供了基礎(chǔ),是映射關(guān)系數(shù)據(jù)到 XML 文檔的重要工具之一。通過(guò) WITH 語(yǔ)句我們還能實(shí)現(xiàn)分層聚集、分列聚集這樣的功能,但復(fù)雜的查詢中很可能會(huì)產(chǎn)生性能問(wèn)題。
清單4.1 XMLAGG一般情況
SELECT
XMLSERIALIZE( CONTENT
XMLELEMENT(NAME "Contacts",
XMLATTRIBUTES(cust_num as "CustNo"),
XMLAGG(
XMLELEMENT(NAME "Name",
XMLATTRIBUTES(cnt_num as "ContactNo"),
RTRIM(f_name)|| ' ' || RTRIM(l_name)
) ORDER BY cnt_num
)
)
AS VARCHAR(5000))
xml FROM contact
GROUP BY cust_num
|
先按客戶分組再按客戶聯(lián)系人所在城市分組聚集:
清單4.2 XMLAGG 嵌套多層聚集
WITH xmlbase as (SELECT
XMLELEMENT(NAME "City",
XMLATTRIBUTES(RTRIM(city) as "name"),
XMLAGG(
XMLELEMENT(NAME "Contact",
XMLATTRIBUTES(cnt_num as "ContactNo"),
RTRIM(f_name)|| ' ' || RTRIM(l_name)
) ORDER BY cnt_num
)
) xml,cust_num,city FROM contact
GROUP BY cust_num,city)
SELECT XMLSERIALIZE(CONTENT
XMLELEMENT(NAME "CustLoc",
XMLATTRIBUTES(xmlbase.cust_num as "CustNo"),
XMLAGG(xml ORDER BY xmlbase.city)
)
AS VARCHAR(500)) FROM xmlbase
GROUP BY xmlbase.cust_num;
|
加入另一示例表 sold_product,包含客戶與產(chǎn)品的聯(lián)系,在 XML 文檔中在每個(gè)客戶節(jié)點(diǎn)下面包含該客戶的所有產(chǎn)品信息與所有聯(lián)系人信息,即將每個(gè)客戶的產(chǎn)品信息和聯(lián)系人信息進(jìn)行并列聚集:
清單4.3 XMLAGG 不同列的聚集
SELECT cust_num, prd_code FROM sold_product;
CUST_NUM PRD_CODE
0000000001 PRODUCT_0A
0000000001 PRODUCT_0B
WITH prd as (
SELECT cust_num,
XMLELEMENT(NAME "Products",
XMLAGG(XMLELEMENT(NAME "Item",prd_code) ORDER BY sp_num)
) xml
FROM sold_product
group by cust_num
),
cnt as (
select cust_num,
XMLELEMENT(NAME "Contacts",
XMLAGG(
XMLELEMENT(NAME "Contact",
XMLATTRIBUTES(contact.cnt_num as "ContactNo"),
RTRIM(f_name)|| ' ' || RTRIM(l_name)
) ORDER BY cnt_num
)
) xml
FROM contact
group by cust_num
)
SELECT
XMLSERIALIZE( CONTENT
XMLELEMENT(NAME "Customer",
XMLATTRIBUTES(prd.cust_num as "CustNo"),
prd.xml,
cnt.xml
)
AS VARCHAR(5000))
xml FROM prd JOIN cnt
ON prd.cust_num=cnt.cust_num
WHERE prd.cust_num='0000000001';
|
2.DB2 示例數(shù)據(jù)準(zhǔn)備與 XML 文檔設(shè)計(jì)
在這里我們將使用一個(gè)完整的示例,來(lái)展示如何應(yīng)用這些 XML 函數(shù)開發(fā) XML 查詢和試圖的基本過(guò)程。我們所設(shè)計(jì)的示例希望使用大部分 XML 函數(shù),展示典型應(yīng)用場(chǎng)景。
在進(jìn)行 XML 視圖的開發(fā)之前,我們?cè)谇懊?contact 示例表的基礎(chǔ)上,準(zhǔn)備好兩個(gè)關(guān)聯(lián)的數(shù)據(jù)表 customer 表與 contact 表,前者與后者是一對(duì)多的關(guān)系,即一條 customer 記錄可以擁有多個(gè)聯(lián)系人記錄。
清單5:示例表及數(shù)據(jù)樣例
Customer表
Contact表
我們要將 customer 表中的每一位顧客的信息連同與之相聯(lián)系的若干 contact 記錄通過(guò)一個(gè) XML 文檔發(fā)布,在我們需要實(shí)現(xiàn)的 XML 文檔結(jié)構(gòu)中必然會(huì)出現(xiàn)一個(gè) Customer 元素中包含若干個(gè) contact 元素的樹狀結(jié)構(gòu),如圖 1 所示。
圖1
在通常的開發(fā)過(guò)程中,我們一般將會(huì)被要求根據(jù)已定義好的 XSD 文檔,來(lái)實(shí)現(xiàn)指定格式的 XML 文檔發(fā)布。但 XSD 定義文件并不是必需的,它只是嚴(yán)密地告訴我們目標(biāo) XML 文檔的結(jié)構(gòu)。實(shí)際上編寫 XML 查詢或視圖時(shí),更應(yīng)該重視 XML 文檔樣例。因?yàn)橛泻芏鄷r(shí)候,設(shè)計(jì)過(guò)程可能只提供 XML 文檔樣例,不過(guò)這已經(jīng)足夠了。
在這里為了將示例的目標(biāo) XML 文檔準(zhǔn)確的表達(dá)出來(lái),我們也使用 XSD 對(duì)其進(jìn)行定義,編寫出定義文件 customer.xsd,如圖2所示。
圖2 XSD(參見下載中的customer.xsd文件)
customer.xsd 文件中定義了在發(fā)布數(shù)據(jù)客戶-聯(lián)系人信息中所需的若干數(shù)據(jù)類型和子結(jié)構(gòu)類型,如 LocationType、EmailType、CityType 等,對(duì)基本的數(shù)據(jù)庫(kù)類型進(jìn)行了結(jié)構(gòu)化封裝,并且規(guī)定了 XML 文檔的元素層次結(jié)構(gòu)。這里根元素為 CustomerInfo,目標(biāo)命名空間定義為:"http://www.ibm.com/schemas/SimpleXMLDemo",這個(gè)定義文件所形成的 XML 文檔結(jié)構(gòu)如圖 3 所示。
圖3 CustomerInfo 文檔結(jié)構(gòu)
圖3中紅色方框所示的部分說(shuō)明在一個(gè) CustomerInfo 文檔中 Contacts 元素節(jié)點(diǎn)下將包含一個(gè)或者多個(gè) Contact 子元素,每一個(gè) Contact 子元素包含一位聯(lián)系人的信息。
現(xiàn)在的 XSD 定義已經(jīng)涉及到了命名空間的聲明,但在此文檔中所有的元素及數(shù)據(jù)類型都屬于"http://www.ibm.com/schemas/SimpleXMLDemo"這一命名空間。一個(gè)更加普遍的情況是,XML 文件的定義使用了來(lái)自不同命名空間的成分,這也是 XML 可重用性的體現(xiàn)。為了模擬這種效果,我們將與 contact 及其子元素有關(guān)的所有定義從 customer.xsd 中分離出來(lái)放入另一個(gè)獨(dú)立的 XSD 文件 contact.xsd 中去,并且給該文件的命名空間設(shè)置為"http://www.ibm.com/schemas/ContactDemo"
圖4 分離后文檔定義結(jié)構(gòu)(參見下載中的contact.xsd文件)
customer.xsd(contact命名空間前綴聲明為cnt):
按照 customer.xsd 文件的定義,我們手工生成一個(gè)樣本 XML 文檔 sample.xml,文檔中僅包含一個(gè) Contact 子節(jié)點(diǎn)。這樣一個(gè)完整的 XML 文檔樣例,在實(shí)際的 XML 查詢編寫過(guò)程中會(huì)起到非常重要的作用,可以給開發(fā)人員一個(gè)直觀的模板。此外,XML 高度的結(jié)構(gòu)化特點(diǎn)也使利用軟件工具來(lái)處理變得非常方便。
清單 6 sample.xml
<CustomerInfo xmlns="http://www.ibm.com/schemas/SimpleXMLDemo"
xmlns:cnt="http://www.ibm.com/schemas/ContactDemo"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.ibm.com/schemas/SimpleXMLDemo customer.xsd">
<CustomerID>0000000001</CustomerID>
<CompanyName>Default Company</CompanyName>
<BusinessArea>AP</BusinessArea>
<PrimaryLocation>
<cnt:PostalCode>100088</cnt:PostalCode>
<cnt:AddressLine1>No. 25, Xin Jie Kou Wai
Street.</cnt:AddressLine1>
<cnt:City>Beijing</cnt:City>
<cnt:State>Beijing</cnt:State>
<cnt:Country>RPC</cnt:Country>
</PrimaryLocation>
<Contacts>
<cnt:Contact isPrimary="FALSE" Seq="2">
<cnt:FirstName>Kang</cnt:FirstName>
<cnt:LastName>Yang</cnt:LastName>
<cnt:JobTitle>CIO</cnt:JobTitle>
<cnt:OfficePhone>58808808</cnt:OfficePhone>
<cnt:Cellphone>13100001122</cnt:Cellphone>
<cnt:Fax>58808999</cnt:Fax>
<cnt:Email>yangkang@sina.com</cnt:Email>
<cnt:Location>
<cnt:PostalCode>100745</cnt:PostalCode>
<cnt:AddressLine1>No 123, Wang Fu Jin
Street</cnt:AddressLine1>
<cnt:AddressLine2>No 322, Dummy
Street</cnt:AddressLine2>
<cnt:City>Beijing</cnt:City>
<cnt:State>Beijing</cnt:State>
<cnt:Country>RPC</cnt:Country>
</cnt:Location>
</cnt:Contact>
</Contacts>
</CustomerInfo>
|
當(dāng)我們得到 XML 示例文檔的時(shí)候,就可以對(duì)照其結(jié)構(gòu)進(jìn)行 XML 查詢或視圖的開發(fā)了,只需要將 XML 文檔中元素與元素的嵌套關(guān)系轉(zhuǎn)化為 XMLELEMENT、XMLATTRIBUTES、XMLNAMESPACES 等 XML 函數(shù)的嵌套關(guān)系。一般的手工修改方法是:將起始標(biāo)簽<element>替換為"XMLELEMENT(NAME "element",";當(dāng)該元素具有屬性或者命名空間聲明,需要緊接著加入相應(yīng)的 XMLATTRIBUTES 或 XMLNAMESPACES 語(yǔ)句;如果這是一個(gè)含有文本內(nèi)容的葉子節(jié)點(diǎn),則要將文本內(nèi)容修改為 SQL 常量(如:'CONSTANT')或者 SQL 字段(如:tbl.colname);最后把結(jié)束標(biāo)簽替換為")",并列的一組元素修改完畢后,應(yīng)用逗號(hào)分隔。為了避免不必要的疏忽,應(yīng)該遵循嵌套的元素從外到內(nèi),并列的元素從前往后的順序在進(jìn)行處理。
3.通過(guò)工具生成 XML 查詢框架
編寫 XML 查詢的過(guò)程如果完全用手工來(lái)實(shí)施,需要開發(fā)者有清晰的思路和良好的編輯習(xí)慣,從技術(shù)難度上講沒(méi)有太多問(wèn)題,但處理較為復(fù)雜的 XML 文檔結(jié)構(gòu)時(shí),開發(fā)人員需要非常仔細(xì)地將 XML 文檔中每個(gè)元素節(jié)點(diǎn)與數(shù)據(jù)庫(kù)中的字段進(jìn)行對(duì)應(yīng),并且根據(jù)樣例組織成復(fù)雜的 XML 結(jié)構(gòu),工作量比較大,變得相當(dāng)麻煩并且容易出錯(cuò)。
作者根據(jù)實(shí)際開發(fā)的 XML 查詢的經(jīng)驗(yàn),編寫了一個(gè)小程序,用于將樣例 XML 文檔轉(zhuǎn)化成 XML 查詢框架,即一個(gè)可以產(chǎn)生目標(biāo)結(jié)構(gòu)的 XML 文檔 SQL 查詢,其中所有的元素、屬性值等都直接使用來(lái)自樣例 XML 中的內(nèi)容轉(zhuǎn)化為常量。生成的 XML/SQL 查詢語(yǔ)句,可以直接在 DB2 中執(zhí)行,在很大程度上減輕了開發(fā)人員的工作量,提高了工作效率。
此程序的代碼附于本文后,可供讀者下載使用或參考。該程序使用 Java 語(yǔ)言完成,只有一個(gè)包含 Main 函數(shù)的類:xmlutol.Xml2DB2view,在處理 XML 文件的過(guò)程中使用了開源的 dom4j 項(xiàng)目。這個(gè)類接受兩個(gè)參數(shù),前一參數(shù)是輸入 XML 文檔的文件名,后一參數(shù)是輸出 SQL 腳本的目標(biāo)文件名。
我們將清單6中的sample.xml傳給該程序處理,可以得到初步的 SQL 框架。
清單 7 調(diào)用 xmlutol.Xml2DB2view
C:\XML2SQL\java -classpath .\bin;.\lib\dom4j-1.6.1.jar
xmlutol.Xml2DB2view .\input\sample.xml .\output\views.sql
[INFO]:XML to SQL Conversion Finished.
[INFO]:Output file is : .\input\views.sql
|
檢查 views.sql 中生成的 SQL 內(nèi)容:
需要被替換的數(shù)據(jù)常量用紅色粗體標(biāo)出
清單 8 生成的 views.sql
SELECT XMLSERIALIZE(CONTENT
XMLELEMENT(NAME "CustomerInfo",
XMLNAMESPACES(
DEFAULT 'http://www.ibm.com/schemas/SimpleXMLDemo',
'http://www.ibm.com/schemas/ContactDemo' as "cnt",
'http://www.w3.org/2001/XMLSchema' as "xsd",
'http://www.w3.org/2001/XMLSchema-instance' as "xsi"
),
XMLATTRIBUTES(
'http://www.ibm.com/schemas/SimpleXMLDemo customer.xsd' as "xsi:schemaLocation"
|-------- XML error: The previous line is longer than the max of 90 characters ---------|
),
XMLELEMENT(NAME "CustomerID",
'0000000001'
),
XMLELEMENT(NAME "CompanyName",
'Default Company'
),
XMLELEMENT(NAME "BusinessArea",
'AP'
),
XMLELEMENT(NAME "PrimaryLocation",
XMLELEMENT(NAME "cnt:PostalCode",
'100088'
),
XMLELEMENT(NAME "cnt:AddressLine1",
'No. 25, Xin Jie Kou Wai Street.'
),
XMLELEMENT(NAME "cnt:City",
'Beijing'
),
XMLELEMENT(NAME "cnt:State",
'Beijing'
),
XMLELEMENT(NAME "cnt:Country",
'RPC'
)
),
XMLELEMENT(NAME "Contacts",
XMLELEMENT(NAME "cnt:Contact",
XMLATTRIBUTES(
'FALSE' as "isPrimary",
'2' as "Seq"
),
XMLELEMENT(NAME "cnt:FirstName",
'Kang'
),
XMLELEMENT(NAME "cnt:LastName",
'Yang'
),
XMLELEMENT(NAME "cnt:JobTitle",
'CIO'
),
XMLELEMENT(NAME "cnt:OfficePhone",
'58808808'
),
XMLELEMENT(NAME "cnt:Cellphone",
'13100001122'
),
XMLELEMENT(NAME "cnt:Fax",
'58808999'
),
XMLELEMENT(NAME "cnt:Email",
'yangkang@sina.com'
),
XMLELEMENT(NAME "cnt:Location",
XMLELEMENT(NAME "cnt:PostalCode",
'100745'
),
XMLELEMENT(NAME "cnt:AddressLine1",
'No 123, Wang Fu Jin Street'
),
XMLELEMENT(NAME "cnt:AddressLine2",
'No 322, Dummy Street'
),
XMLELEMENT(NAME "cnt:City",
'Beijing'
),
XMLELEMENT(NAME "cnt:State",
'Beijing'
),
XMLELEMENT(NAME "cnt:Country",
'RPC'
)
)
)
)
)
as VARCHAR(5000)) FROM sysibm.sysdummy1;
|
在接下來(lái)的過(guò)程中,需要將查詢中的引用的 sysibm.sysdummy1 表、常數(shù)內(nèi)容等替換成目標(biāo)表和相應(yīng)的數(shù)據(jù)列,如果需要還應(yīng)該加入聚集、Null 值處理等邏輯, 以及使用 XMLFOREST 函數(shù)對(duì)查詢進(jìn)行精簡(jiǎn)。由于這些修改非常靈活,而且對(duì)于每個(gè)任務(wù)可能會(huì)很特殊,所以不便用簡(jiǎn)單的程序來(lái)實(shí)現(xiàn),所幸這部分修改的工作量相對(duì)較小。
4.修改 XML 框架完成 XML 查詢
以清單 8 中的查詢腳本為基礎(chǔ),通過(guò)適當(dāng)?shù)男薷模涂梢酝瓿砂l(fā)布 Customer 與 Contact 數(shù)據(jù)的 XML 查詢。
在得到 XML 查詢框架之后,就應(yīng)寫出讀取目標(biāo)數(shù)據(jù)的常規(guī) SQL,如果覺得有必要還可以根據(jù)此查詢整理出從查詢數(shù)據(jù)列到 XML 文檔元素的映射關(guān)系列表,方便后續(xù)的修改。
清單 10 SQL 常規(guī)查詢,及其 XML 映射關(guān)系
SELECT
--customer columns
cust.cust_num,cust.cust_name,cust.language,
cust.area,cust.address,cust.city,
cust.state,cust.postal_code,cust.country_code,
--contact columns
cnt.cnt_num,cnt.f_name,cnt.l_name,
cnt.title,cnt.office_phone,cnt.cellphone,
cnt.fax,cnt.email,cnt.address,
cnt.address2,cnt.city,cnt.state,
cnt.postal_code,cnt.country_code,cnt.prim_flag
FROM
customer cust
JOIN contact cnt
ON cust.cust_num = cnt.cust_num
|
根據(jù)上面的列表,我們逐一將 XML 查詢框架中的常量用 SQL 查詢的字段替換,同時(shí)也用 SQL 查詢的 From 語(yǔ)句塊替換掉 XML 查詢框架的"FROM sysibm.sysdummy1"部分。
聚集處理:
將這個(gè)查詢應(yīng)用入 XML 查詢時(shí),由于 customer 與 contact 是一對(duì)多的關(guān)系,根據(jù)設(shè)計(jì) XML 查詢中的單條記錄對(duì)應(yīng)一條 customer 記錄,所以我們需要在 XML 查詢中對(duì)來(lái)自 contact 表的部分進(jìn)行聚集,即對(duì) <cnt:Contact> 元素進(jìn)行聚集。
清單 11 <cnt:Contact> 聚集片斷
SELECT ……
XMLAGG(
XMLELEMENT(NAME "cnt:Contact",
……
……
) ORDER BY cnt.cnt_num
) ……
FROM ……
……
Group by cnt.cnt_num, ……
|
NULL 值處理:
通常情況下,XML文檔中某些可選元素的源數(shù)據(jù)在數(shù)據(jù)庫(kù)中可能為空值,而XMLELEMENT函數(shù)接收NULL值,會(huì)在XML文檔中產(chǎn)生形如<Element></Element>的空節(jié)點(diǎn)。對(duì)于XML文檔的處理時(shí),我們一般不希望顯示這樣的空節(jié)點(diǎn)。對(duì)于這樣的情況,可以通過(guò)清單12的方式進(jìn)行處理。
清單12 NULL 值處理
XMLELEMENT(NAME "cnt:Location",
XMLELEMENT(NAME "cnt:AddressLine1",
cnt.address
),
CASE
WHEN RTRIM(COALESCE(cnt.address2,''))<>'' THEN
XMLELEMENT(NAME "cnt:AddressLine2",
cnt.address2
)
ELSE NULL END
)
|
當(dāng) cnt.address2 為空值時(shí),上述的語(yǔ)句中的"cnt:AddressLine2"節(jié)點(diǎn)將不會(huì)出現(xiàn)在生成的 XML 中,XML 結(jié)構(gòu)將是:
<cnt:Location><cnt:AddressLine1>4F, Sunshine Building</cnt:Location>
</cnt:AddressLine1>
|
如果有很多可選節(jié)點(diǎn),也可以通過(guò)程序來(lái)生成框架,只需要給樣例 XML 文檔的相應(yīng)節(jié)點(diǎn)添加 optional 屬性并賦為"true",譬如 <BusinessArea optional="true">AP</BusinessArea>,通過(guò)我們的程序即可完成對(duì)其的處理。
經(jīng)過(guò)上述修改就完成了最終的 XML 查詢語(yǔ)句,在清單 13 中,已把在生成的 XML 查詢框架基礎(chǔ)上的修改用藍(lán)色粗體標(biāo)注出來(lái),其中 BusinessArea,cnt:JobTitle,cnt:Cellphone,cnt:Location ,cnt:Fax 和 cnt:AddressLine2 元素為可選節(jié)點(diǎn)。
清單13 最終 XML 查詢
SELECT XMLSERIALIZE(CONTENT
XMLELEMENT(NAME "CustomerInfo",
XMLNAMESPACES(
DEFAULT 'http://www.ibm.com/schemas/SimpleXMLDemo',
'http://www.ibm.com/schemas/ContactDemo' as "cnt",
'http://www.w3.org/2001/XMLSchema' as "xsd",
'http://www.w3.org/2001/XMLSchema-instance' as "xsi"
),
XMLATTRIBUTES(
'http://www.ibm.com/schemas/SimpleXMLDemo customer2.xsd' as "xsi:schemaLocation"
|-------- XML error: The previous line is longer than the max of 90 characters ---------|
),
XMLELEMENT(NAME "CustomerID",
cust.cust_num
),
XMLELEMENT(NAME "CompanyName",
cust.cust_name
),
CASE
WHEN RTRIM(COALESCE(cust.area,''))<>'' THEN
XMLELEMENT(NAME "BusinessArea",
cust.area
)
ELSE NULL END,
XMLELEMENT(NAME "PrimaryLocation",
XMLELEMENT(NAME "cnt:PostalCode",
cust.postal_code
),
XMLELEMENT(NAME "cnt:AddressLine1",
cust.address
),
XMLELEMENT(NAME "cnt:City",
cust.city
),
XMLELEMENT(NAME "cnt:State",
cust.state
),
XMLELEMENT(NAME "cnt:Country",
cust.country_code
)
),
XMLELEMENT(NAME "Contacts",
XMLAGG(
XMLELEMENT(NAME "cnt:Contact",
XMLATTRIBUTES(
(CASE
WHEN cnt.prim_flag=1 THEN 'TRUE'
ELSE 'FALSE' END) as "isPrimary",
cnt.cnt_num as "Seq"
),
XMLELEMENT(NAME "cnt:FirstName",
cnt.f_name
),
XMLELEMENT(NAME "cnt:LastName",
cnt.l_name
),
CASE
WHEN RTRIM(COALESCE(cnt.title,''))<>'' THEN
XMLELEMENT(NAME "cnt:JobTitle",
cnt.title
)
ELSE NULL END,
XMLELEMENT(NAME "cnt:OfficePhone",
cnt.office_phone
),
CASE
WHEN RTRIM(COALESCE(cnt.cellphone,''))<>'' THEN
XMLELEMENT(NAME "cnt:Cellphone",
cnt.cellphone
)
ELSE NULL END,
CASE
WHEN RTRIM(COALESCE(cnt.fax,''))<>'' THEN
XMLELEMENT(NAME "cnt:Fax",
cnt.fax
)
ELSE NULL END,
XMLELEMENT(NAME "cnt:Email",
cnt.email
),
CASE
WHEN RTRIM(COALESCE(cnt.address,''))<>'' THEN
XMLELEMENT(NAME "cnt:Location",
XMLELEMENT(NAME "cnt:PostalCode",
cnt.postal_code
),
XMLELEMENT(NAME "cnt:AddressLine1",
cnt.address
),
CASE
WHEN RTRIM(COALESCE(cnt.address2,''))<>'' THEN
XMLELEMENT(NAME "cnt:AddressLine2",
cnt.address2
)
ELSE NULL END,
XMLELEMENT(NAME "cnt:City",
cnt.city
),
XMLELEMENT(NAME "cnt:State",
cnt.state
),
XMLELEMENT(NAME "cnt:Country",
cnt.country_code
)
)
ELSE NULL END
) ORDER BY cnt.cnt_num
)
)
)
as VARCHAR(5000)) FROM
customer cust
JOIN contact cnt
ON cust.cust_num = cnt.cust_num
GROUP BY
cust.cust_num,cust.cust_name,cust.language,
cust.area,cust.address,cust.city,
cust.state,cust.postal_code,cust.country_code
;
|
根據(jù)這個(gè)查詢得到 XML 文檔,參見下載中的 result.xml。
總結(jié)
本文主要介紹了 DB2 提供的基本的 XML 函數(shù),并且給出了具體的開發(fā)實(shí)例,讀者可以通過(guò)本文了解如何根據(jù) XSD 文檔或者 XML 樣例進(jìn)行試圖和查詢的開發(fā)。另外,DB2 Extender 提供了更為方便的創(chuàng)建 XML 視圖或查詢的功能,但是一般情況下需要對(duì)現(xiàn)有數(shù)據(jù)庫(kù)進(jìn)行升級(jí)和配置。而在一個(gè)產(chǎn)品的生命周期中,會(huì)存在多個(gè)數(shù)據(jù)庫(kù)服務(wù)器作為開發(fā)、測(cè)試、維護(hù)和產(chǎn)品運(yùn)行所使用。為這些數(shù)據(jù)庫(kù)增加 Extender 功能會(huì)增加 DBA 的工作量,還會(huì)增加產(chǎn)品環(huán)境停止服務(wù)的時(shí)間。但是通過(guò)本文的介紹,并結(jié)合筆者開發(fā)的工具使用,只利用 DB2 提供的基本 XML 函數(shù),同樣可以很方便快捷的開發(fā) XML 視圖和查詢。
下載
名字 |
大小 |
下載方法 |
0609zhangdw.zip |
298K |
HTTP
|
作者簡(jiǎn)介
|
|
|
陳力,北京師范大學(xué)信息管理專業(yè)研究生,在IBM CSDL Beijing的DSW ODS組實(shí)習(xí)。熟悉DB2,J2EE等技術(shù),對(duì)Web應(yīng)用、Web Services以及Java技術(shù)很感興趣。
|
|
|
|
張大為,IBM CSDL 軟件工程師. 目前從事DB2相關(guān)工作,主要對(duì)電子商務(wù)應(yīng)用進(jìn)行數(shù)據(jù)支持。對(duì)DB2、Unix、Web Services以及Java技術(shù)很感興趣。
|