過去兩年,XML 在數據庫中的定位發生了質變——不再是需「削足適履」融入關係模型的「臨時工」,而是成為一等公民。DB2 pureXML 讓 XML 保持層次結構,同時享受關係引擎的 ACID、索引、觸發器、約束等成熟能力。更有甚者,關係模型開始借鑑 XML 技術(如 JSON 支持)以獲取層次建模優勢。
DB2 允許在同一張表中同時存放關係列與 XML 列。XML 內部以解析後的樹結構存儲,邏輯上卻像 CLOB/BLOB 一樣佔據單一列。這極大簡化了模式設計:
CREATE TABLE CUSTOMER_TABLE (
CUSTOMERID CHAR(12) NOT NULL,
CUSTXML XML NOT NULL,
PRIMARY KEY (CUSTOMERID)
);
CREATE TABLE PURCHASE_TABLE (
CUSTOMERID CHAR(12) NOT NULL,
ITEMXML XML NOT NULL,
FOREIGN KEY (CUSTOMERID) REFERENCES CUSTOMER_TABLE(CUSTOMERID)
);

插入數據與關係列無異:
INSERT INTO CUSTOMER_TABLE VALUES ('hardeep',
'<Customer customerid="hardeep" firstname="hardeep" lastname="singh"/>');
Java 中亦同樣使用 PreparedStatement.setCharacterStream 寫入 XML 文件流。

展開 XML 列,模式從關係轉為層次。統一模式下,可用 XPath 定位 XML 內部節點。例如查找姓氏為 "singh" 的客戶:
SELECT CUSTXML FROM CUSTOMER_TABLE
WHERE XMLEXISTS('$cust/Customer[@lastname="singh"]' PASSING CUSTXML AS "cust");
參數化查詢(Java):
SELECT CUSTXML FROM CUSTOMER_TABLE
WHERE XMLEXISTS('$cust/Customer[@lastname=$lname]'
PASSING CUSTXML AS "cust", CAST(? AS VARCHAR(12)) AS "lname");
Viper 2 簡化了 PASSING 子句,可直接引用列名:
SELECT CUSTXML FROM CUSTOMER_TABLE
WHERE XMLEXISTS('$CUSTXML/Customer[@lastname=$lname]'
PASSING CAST(? AS VARCHAR(12)) AS "lname");
XQuery 支持條件、循環、變量、函數、算術運算,且內建豐富 XSLT 映射能力,可在查詢中完成轉換。以合併客戶與購買記錄為例:

VALUES(XMLQUERY(
'for $Customer in db2-fn:xmlcolumn("CUSTOMER_TABLE.CUSTXML")/Customer
where $Customer/@customerid = $customerid
return <Customer customerid="{$Customer/@customerid}"
firstname="{$Customer/@firstname}"
lastname="{$Customer/@lastname}">{
for $Item in db2-fn:xmlcolumn("PURCHASE_TABLE.ITEMXML")/Customer
where $Item/@customerid = $Customer/@customerid
return $Item/Item
}</Customer>'
PASSING CAST(? AS VARCHAR(255)) AS "customerid"));
Viper 2 引入 XQuery Update,可直接在現有文檔插入節點,避免重構外層元素:
VALUES(XMLQUERY(
'for $Customer in db2-fn:xmlcolumn("CUSTOMER_TABLE.CUSTXML")/Customer
let $items := <Items>{
for $Item in db2-fn:xmlcolumn("PURCHASE_TABLE.ITEMXML")/Customer
where $Item/@customerid = $Customer/@customerid
return $Item/Item
}</Items>
where $Customer/@customerid = $customerid
return transform copy $cust := $Customer
modify do insert $items as last into $cust
return $cust'
PASSING 'hardeep' AS "customerid"));

CREATE PROCEDURE customerItems(IN custid VARCHAR(12))
DYNAMIC RESULT SETS 1 LANGUAGE SQL
BEGIN
DECLARE c_cur CURSOR WITH RETURN FOR
VALUES(XMLQUERY(... PASSING custid AS "customerid"));
OPEN c_cur;
END
應用層代碼縮減為:
ResultSet rs = stmt.executeQuery("CALL customerItems(?)");
XMLParse xml = new XMLParse(rs.getString(1));
保險費率隨價格、日期變化,以 XML 形式從 Web Service 獲取:
<insurance>
<rate price="100" currency="$" rate=".02"/>
<rate price="500" currency="$" rate=".018"/>
<rate price="" currency="$" rate=".015"/>
</insurance>
存儲過程接收 rate XML 參數,內部用 XQuery 條件表達式計算每筆訂單保費並插入結果 XML。這證明:XML 數據既可來自表列、函數生成,也可作為運行時參數傳入,數據庫與應用服務器的界限正在模糊。
即使產品說明存放在關係表 SQLPRODUCT,仍可通過 XMLExists 聯結 XML 列:
SELECT details, weight FROM SQLPRODUCT, ITEM_TABLE
WHERE XMLEXISTS('$itemXML/item[@itemid=$pid]'
PASSING ITEM_TABLE.ITEMXML AS "itemXML", SQLPRODUCT.PID AS "pid");

Viper 2 允許向 XQuery 嵌入的 SQL 傳遞參數:
VALUES(XMLQUERY(
'for $Item in db2-fn:xmlcolumn("PURCHASE_TABLE.ITEMXML")/Customer
where $Item/@customerid = $custid
return ($Item/Item,
db2-fn:sqlquery(
''SELECT XMLROW(details, description, weight OPTION ROW "description")
FROM sqlproduct WHERE pid = parameter(1)'',
$Item/Item/@ID))'
PASSING CAST(? AS VARCHAR(255)) AS "custid"));
CREATE INDEX custfname ON customer_table(info) GENERATE KEY USING XMLPATTERN '/Customer/@firstname' AS SQL VARCHAR(64)XMLTABLE 提取關鍵字段建關係索引XML 已成行業標準。pureXML 讓數據庫原生支持索引、查詢、驗證、轉換、更新 XML。隨著更多邏輯下推,數據庫可直接發布存儲過程為 Web Service,成為 SOA 活躍節點。舊秩序已變,新秩序正在形成。