空間站

          北極心空

            BlogJava :: 首頁 :: 聯(lián)系 :: 聚合  :: 管理
            15 Posts :: 393 Stories :: 160 Comments :: 0 Trackbacks

          級別: 初級

          陳 力 (chenli44@yeah.net), 北京師范大學(xué)信息管理專業(yè)研究生
          張 大為 (zhangdaw@cn.ibm.com), 軟件工程師

          2006 年 9 月 21 日

          摘要:本文將介紹 DB2 提供的一些基本 XML 函數(shù),并結(jié)合一個簡單的實例,重點介紹如何利用 DB2 提供的 XML 函數(shù)以視圖或查詢的形式靈活的實現(xiàn) XML 文檔的構(gòu)造和發(fā)布。

          DB2 提供了豐富的功能用以發(fā)布 XML 格式的數(shù)據(jù),這種功能包括了 DB2 內(nèi)置的 XML 函數(shù)以及 XML extender 提供的功能(包括了發(fā)布、解析、檢索、存儲等多種功能)。對 XML 文檔的支持,使得利用現(xiàn)有的 DB2 數(shù)據(jù)庫,就能夠方便的實現(xiàn)系統(tǒng)或者數(shù)據(jù)的整合。

          本文讀者定位為具有一定的 DB2 開發(fā)經(jīng)驗,同時對 XML 語言的格式、schema 定義、命名空間等有基本的理解。

          本文將介紹 DB2 提供的一些基本 XML 函數(shù),并結(jié)合一個簡單的實例,重點介紹如何利用 DB2 提供的 XML 函數(shù)以視圖或查詢的形式靈活的實現(xiàn) XML 文檔的構(gòu)造和發(fā)布。同時文中還將介紹利用作者編制的一個工具,根據(jù)目標(biāo) XML 的樣例文檔生成相應(yīng)的包含 XML 函數(shù)的查詢框架,以輔助開發(fā)較為復(fù)雜的 XML 文檔結(jié)構(gòu)的 SQL 語句。

          以 DB2 的 XML 函數(shù)為基礎(chǔ)的 SQL 語句支持 XML 語言的絕大部分特征,可以簡單而直觀地實現(xiàn)從關(guān)系型數(shù)據(jù)模型到 XML 的樹狀結(jié)構(gòu)的轉(zhuǎn)換,其靈活性接近于手工書寫 XML 文檔,同時,只要利用適當(dāng)?shù)姆椒ê凸ぞ?,就可以在不增加開發(fā)復(fù)雜度的基礎(chǔ)上大大降低開發(fā)的工作量。

          1.DB2 XML 查詢/視圖及 XML 函數(shù)簡介

          利用 XML 函數(shù)我們可以方便的構(gòu)建 XML 查詢或視圖,利用這些函數(shù)發(fā)布 XML 的整個過程與開發(fā)和使用傳統(tǒng)的查詢與視圖并沒有本質(zhì)的區(qū)別。XML 查詢不需要依賴于復(fù)雜的發(fā)布程序和軟件平臺,即使只有 DB2 命令行客戶端,我們也能夠連接數(shù)據(jù)庫發(fā)布 XML 文檔??梢娎眠@種方式進(jìn)行 XML 發(fā)布具有很廣泛的應(yīng)用范圍,能夠適應(yīng)從高級語言開發(fā)的網(wǎng)絡(luò)程序到簡單的 Shell 腳本這樣不同級別實現(xiàn)的要求,而且特別有利于簡單直接的解決方案。這給我們以 DB2 數(shù)據(jù)庫為基礎(chǔ)實現(xiàn)異構(gòu)數(shù)據(jù)系統(tǒng)的整合與互動功能提供了一個簡便易行的方法。

          DB2 UDB 8.2 直接提供了 7 種 SQL/XML 函數(shù),以及與這些 XML 語法成分對應(yīng)的 XML 數(shù)據(jù)類型。

          XML函數(shù)列表:

          XMLSERIALIZE
          XMLELEMENT
          XMLATTRIBUTES
          XMLNAMESPACES
          XMLFOREST
          XMLCONCAT
          XMLAGG

          除了 XMLSERIALIZE 之外,其他六個函數(shù)均返回 XML 數(shù)據(jù)類型,包括了元素、屬性、命名空間、節(jié)點集。利用這些函數(shù)提供的 XML 成分,我們可以構(gòu)造出任意指定的 XML 文檔結(jié)構(gòu)。DB2 的 XML 函數(shù)已經(jīng)提供了足夠的功能,可以認(rèn)為只要設(shè)計人員能從業(yè)務(wù)邏輯上實現(xiàn)從關(guān)系數(shù)據(jù)模型到 XML 文檔樹狀結(jié)構(gòu)的映射關(guān)系的設(shè)計,開發(fā)人員就能夠利用這些基本函數(shù)直接實現(xiàn) XML 文檔的生成。

          我們在這里使用簡單的示例對上述 XML 函數(shù)一一進(jìn)行介紹。我們在本地數(shù)據(jù)庫中創(chuàng)建了一個簡單的 Contact 表,并且插入了幾條數(shù)據(jù),作為示例基礎(chǔ):


          SELECT cust_num, f_name, l_name, email, cnt_num FROM contact
          SELECT cust_num, f_name, l_name, email, cnt_num FROM contact

          XMLSERIALIZE:

          XMLSERIALIZE 函數(shù)是一個特殊的函數(shù),并不產(chǎn)生 XML 文檔的一部分,而是用于將其他函數(shù)產(chǎn)生的 XML 數(shù)據(jù)類型轉(zhuǎn)化為其他的常見數(shù)據(jù)類型,如 CHAR、VARCHAR 和 CLOB 等。由于 XML 數(shù)據(jù)類型不能直接存儲到數(shù)據(jù)庫中,也不能直接輸出,通常都需要使用 XMLSERIALIZE 函數(shù)進(jìn)行轉(zhuǎn)換,后面的示例也都會使用到它。

          XMLELEMENT:

          XMLELEMENT 是最基礎(chǔ)的 XML 函數(shù),用于構(gòu)建 XML 元素節(jié)點,這個函數(shù)接受元素名稱和元素內(nèi)容兩個必選參數(shù),另外還可以接受 XML 屬性和命名空間參數(shù)。元素內(nèi)容可以是 CHAR、VARCHAR、INT 等基本數(shù)據(jù)類型,也可以是 XML 元素數(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ù)后可以為該元素添加屬性,而后者則可以向元素中添加命名空間以及命名空間前綴的聲明,多個屬性/命名空間可以通過一個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é)點使之具有相同的內(nèi)部XML數(shù)據(jù)類型,如<elem1>…</elem1><elem2>…</elem2><elem3>…</elem3>這樣的形式。XMLFOREST用于根據(jù)輸入的多個一般類型值參數(shù)生成簡單的文本節(jié)點"森林",XMLCONCAT接受的參數(shù)只能是XML數(shù)據(jù)類型,可以用于將許多復(fù)雜的元素節(jié)點組合在一起形成并列的節(jié)點結(jié)構(gòu)。


          清單3 XMLFOREST與XMLCONCAT對比
          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
          



          這兩個函數(shù)雖然結(jié)果形式類似,但是其功能卻是不同的。XMLFOREST 只能生成簡單值元素的"森林",不能靈活的設(shè)置這些值元素的屬性和命名空間聲明等,但是它語法非常簡單,非常適合用在處理多個葉子值節(jié)點并列出現(xiàn)的情況。而 XMLCONCAT 能將多個復(fù)雜的 XML 元素節(jié)點形成并列的節(jié)點結(jié)構(gòu)。XMLSERIALIZE 只能接受一個 XML 數(shù)據(jù)類型的參數(shù),在上述例子中 XMLCONCAT 用來將多個 XML 元素形成并列節(jié)點結(jié)構(gòu)并且返回一個相同的 XML 數(shù)據(jù)類型傳給 XMLSERIALIZE,如果不使用 XMLCONCAT 將無法成功調(diào)用 XMLSERIALIZE。

          XMLAGG:

          XMLAGG 是 XML 函數(shù)中唯一的聚集函數(shù),XMLAGG 函數(shù)可以將若干條記錄中的列值按照 XML 結(jié)構(gòu)的定義聚集成為"森林"結(jié)構(gòu),合成到一條記錄中。XMLAGG 為我們根據(jù)數(shù)據(jù)實現(xiàn)動態(tài)樹狀結(jié)構(gòu)提供了基礎(chǔ),是映射關(guān)系數(shù)據(jù)到 XML 文檔的重要工具之一。通過 WITH 語句我們還能實現(xiàn)分層聚集、分列聚集這樣的功能,但復(fù)雜的查詢中很可能會產(chǎ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 文檔中在每個客戶節(jié)點下面包含該客戶的所有產(chǎn)品信息與所有聯(lián)系人信息,即將每個客戶的產(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è)計

          在這里我們將使用一個完整的示例,來展示如何應(yīng)用這些 XML 函數(shù)開發(fā) XML 查詢和試圖的基本過程。我們所設(shè)計的示例希望使用大部分 XML 函數(shù),展示典型應(yīng)用場景。

          在進(jìn)行 XML 視圖的開發(fā)之前,我們在前面 contact 示例表的基礎(chǔ)上,準(zhǔn)備好兩個關(guān)聯(lián)的數(shù)據(jù)表 customer 表與 contact 表,前者與后者是一對多的關(guān)系,即一條 customer 記錄可以擁有多個聯(lián)系人記錄。

          清單5:示例表及數(shù)據(jù)樣例


          Customer表
          Customer表

          Contact表
          Contact表

          我們要將 customer 表中的每一位顧客的信息連同與之相聯(lián)系的若干 contact 記錄通過一個 XML 文檔發(fā)布,在我們需要實現(xiàn)的 XML 文檔結(jié)構(gòu)中必然會出現(xiàn)一個 Customer 元素中包含若干個 contact 元素的樹狀結(jié)構(gòu),如圖 1 所示。


          圖1
          圖1

          在通常的開發(fā)過程中,我們一般將會被要求根據(jù)已定義好的 XSD 文檔,來實現(xiàn)指定格式的 XML 文檔發(fā)布。但 XSD 定義文件并不是必需的,它只是嚴(yán)密地告訴我們目標(biāo) XML 文檔的結(jié)構(gòu)。實際上編寫 XML 查詢或視圖時,更應(yīng)該重視 XML 文檔樣例。因為有很多時候,設(shè)計過程可能只提供 XML 文檔樣例,不過這已經(jīng)足夠了。

          在這里為了將示例的目標(biāo) XML 文檔準(zhǔn)確的表達(dá)出來,我們也使用 XSD 對其進(jìn)行定義,編寫出定義文件 customer.xsd,如圖2所示。

          圖2 XSD(參見下載中的customer.xsd文件)



          customer.xsd 文件中定義了在發(fā)布數(shù)據(jù)客戶-聯(lián)系人信息中所需的若干數(shù)據(jù)類型和子結(jié)構(gòu)類型,如 LocationType、EmailType、CityType 等,對基本的數(shù)據(jù)庫類型進(jìn)行了結(jié)構(gòu)化封裝,并且規(guī)定了 XML 文檔的元素層次結(jié)構(gòu)。這里根元素為 CustomerInfo,目標(biāo)命名空間定義為:"http://www.ibm.com/schemas/SimpleXMLDemo",這個定義文件所形成的 XML 文檔結(jié)構(gòu)如圖 3 所示。


          圖3 CustomerInfo 文檔結(jié)構(gòu)
          圖3  CustomerInfo 文檔結(jié)構(gòu)

          圖3中紅色方框所示的部分說明在一個 CustomerInfo 文檔中 Contacts 元素節(jié)點下將包含一個或者多個 Contact 子元素,每一個 Contact 子元素包含一位聯(lián)系人的信息。

          現(xiàn)在的 XSD 定義已經(jīng)涉及到了命名空間的聲明,但在此文檔中所有的元素及數(shù)據(jù)類型都屬于"http://www.ibm.com/schemas/SimpleXMLDemo"這一命名空間。一個更加普遍的情況是,XML 文件的定義使用了來自不同命名空間的成分,這也是 XML 可重用性的體現(xiàn)。為了模擬這種效果,我們將與 contact 及其子元素有關(guān)的所有定義從 customer.xsd 中分離出來放入另一個獨立的 XSD 文件 contact.xsd 中去,并且給該文件的命名空間設(shè)置為"http://www.ibm.com/schemas/ContactDemo"

          圖4 分離后文檔定義結(jié)構(gòu)(參見下載中的contact.xsd文件)



          customer.xsd(contact命名空間前綴聲明為cnt):



          按照 customer.xsd 文件的定義,我們手工生成一個樣本 XML 文檔 sample.xml,文檔中僅包含一個 Contact 子節(jié)點。這樣一個完整的 XML 文檔樣例,在實際的 XML 查詢編寫過程中會起到非常重要的作用,可以給開發(fā)人員一個直觀的模板。此外,XML 高度的結(jié)構(gòu)化特點也使利用軟件工具來處理變得非常方便。


          清單 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 示例文檔的時候,就可以對照其結(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 語句;如果這是一個含有文本內(nèi)容的葉子節(jié)點,則要將文本內(nèi)容修改為 SQL 常量(如:'CONSTANT')或者 SQL 字段(如:tbl.colname);最后把結(jié)束標(biāo)簽替換為")",并列的一組元素修改完畢后,應(yīng)用逗號分隔。為了避免不必要的疏忽,應(yīng)該遵循嵌套的元素從外到內(nèi),并列的元素從前往后的順序在進(jìn)行處理。





          回頁首


          3.通過工具生成 XML 查詢框架

          編寫 XML 查詢的過程如果完全用手工來實施,需要開發(fā)者有清晰的思路和良好的編輯習(xí)慣,從技術(shù)難度上講沒有太多問題,但處理較為復(fù)雜的 XML 文檔結(jié)構(gòu)時,開發(fā)人員需要非常仔細(xì)地將 XML 文檔中每個元素節(jié)點與數(shù)據(jù)庫中的字段進(jìn)行對應(yīng),并且根據(jù)樣例組織成復(fù)雜的 XML 結(jié)構(gòu),工作量比較大,變得相當(dāng)麻煩并且容易出錯。

          作者根據(jù)實際開發(fā)的 XML 查詢的經(jīng)驗,編寫了一個小程序,用于將樣例 XML 文檔轉(zhuǎn)化成 XML 查詢框架,即一個可以產(chǎn)生目標(biāo)結(jié)構(gòu)的 XML 文檔 SQL 查詢,其中所有的元素、屬性值等都直接使用來自樣例 XML 中的內(nèi)容轉(zhuǎn)化為常量。生成的 XML/SQL 查詢語句,可以直接在 DB2 中執(zhí)行,在很大程度上減輕了開發(fā)人員的工作量,提高了工作效率。

          此程序的代碼附于本文后,可供讀者下載使用或參考。該程序使用 Java 語言完成,只有一個包含 Main 函數(shù)的類:xmlutol.Xml2DB2view,在處理 XML 文件的過程中使用了開源的 dom4j 項目。這個類接受兩個參數(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;
          

          在接下來的過程中,需要將查詢中的引用的 sysibm.sysdummy1 表、常數(shù)內(nèi)容等替換成目標(biāo)表和相應(yīng)的數(shù)據(jù)列,如果需要還應(yīng)該加入聚集、Null 值處理等邏輯, 以及使用 XMLFOREST 函數(shù)對查詢進(jìn)行精簡。由于這些修改非常靈活,而且對于每個任務(wù)可能會很特殊,所以不便用簡單的程序來實現(xiàn),所幸這部分修改的工作量相對較小。





          回頁首


          4.修改 XML 框架完成 XML 查詢

          以清單 8 中的查詢腳本為基礎(chǔ),通過適當(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 查詢的字段替換,同時也用 SQL 查詢的 From 語句塊替換掉 XML 查詢框架的"FROM sysibm.sysdummy1"部分。

          聚集處理:

          將這個查詢應(yīng)用入 XML 查詢時,由于 customer 與 contact 是一對多的關(guān)系,根據(jù)設(shè)計 XML 查詢中的單條記錄對應(yīng)一條 customer 記錄,所以我們需要在 XML 查詢中對來自 contact 表的部分進(jìn)行聚集,即對 <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ù)庫中可能為空值,而XMLELEMENT函數(shù)接收NULL值,會在XML文檔中產(chǎn)生形如<Element></Element>的空節(jié)點。對于XML文檔的處理時,我們一般不希望顯示這樣的空節(jié)點。對于這樣的情況,可以通過清單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 為空值時,上述的語句中的"cnt:AddressLine2"節(jié)點將不會出現(xiàn)在生成的 XML 中,XML 結(jié)構(gòu)將是:


          <cnt:Location><cnt:AddressLine1>4F, Sunshine Building</cnt:Location>
          </cnt:AddressLine1>
          

          如果有很多可選節(jié)點,也可以通過程序來生成框架,只需要給樣例 XML 文檔的相應(yīng)節(jié)點添加 optional 屬性并賦為"true",譬如 <BusinessArea optional="true">AP</BusinessArea>,通過我們的程序即可完成對其的處理。

          經(jīng)過上述修改就完成了最終的 XML 查詢語句,在清單 13 中,已把在生成的 XML 查詢框架基礎(chǔ)上的修改用藍(lán)色粗體標(biāo)注出來,其中 BusinessArea,cnt:JobTitle,cnt:Cellphone,cnt:Location ,cnt:Fax 和 cnt:AddressLine2 元素為可選節(jié)點。


          清單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ù)這個查詢得到 XML 文檔,參見下載中的 result.xml。





          回頁首


          總結(jié)

          本文主要介紹了 DB2 提供的基本的 XML 函數(shù),并且給出了具體的開發(fā)實例,讀者可以通過本文了解如何根據(jù) XSD 文檔或者 XML 樣例進(jìn)行試圖和查詢的開發(fā)。另外,DB2 Extender 提供了更為方便的創(chuàng)建 XML 視圖或查詢的功能,但是一般情況下需要對現(xiàn)有數(shù)據(jù)庫進(jìn)行升級和配置。而在一個產(chǎn)品的生命周期中,會存在多個數(shù)據(jù)庫服務(wù)器作為開發(fā)、測試、維護(hù)和產(chǎn)品運行所使用。為這些數(shù)據(jù)庫增加 Extender 功能會增加 DBA 的工作量,還會增加產(chǎn)品環(huán)境停止服務(wù)的時間。但是通過本文的介紹,并結(jié)合筆者開發(fā)的工具使用,只利用 DB2 提供的基本 XML 函數(shù),同樣可以很方便快捷的開發(fā) XML 視圖和查詢。






          回頁首


          下載

          名字 大小 下載方法
          0609zhangdw.zip 298K HTTP
          關(guān)于下載方法的信息 Get Adobe? Reader?




          回頁首






          回頁首


          作者簡介

          陳力,北京師范大學(xué)信息管理專業(yè)研究生,在IBM CSDL Beijing的DSW ODS組實習(xí)。熟悉DB2,J2EE等技術(shù),對Web應(yīng)用、Web Services以及Java技術(shù)很感興趣。


          張大為,IBM CSDL 軟件工程師. 目前從事DB2相關(guān)工作,主要對電子商務(wù)應(yīng)用進(jìn)行數(shù)據(jù)支持。對DB2、Unix、Web Services以及Java技術(shù)很感興趣。

          posted on 2006-11-27 18:10 蘆葦 閱讀(1642) 評論(0)  編輯  收藏 所屬分類: 其他
          主站蜘蛛池模板: 绿春县| 平乐县| 崇阳县| 梧州市| 新密市| 长海县| 四平市| 罗田县| 齐齐哈尔市| 治县。| 永丰县| 新竹县| 东海县| 深圳市| 朝阳市| 江达县| 安仁县| 黑水县| 冀州市| 台北县| 房产| 大港区| 鄂托克前旗| 连州市| 会东县| 广德县| 旅游| 鲁甸县| 汤原县| 和硕县| 越西县| 德江县| 内丘县| 凯里市| 汝阳县| 炎陵县| 陆良县| 兴隆县| 宁南县| 丹巴县| 阿巴嘎旗|