走進(jìn) SQL/XML作者:Tim Quinlan
概覽 SQL 與 XML 的互操作性,了解如何著手處理存儲(chǔ)在數(shù)據(jù)庫中的 XML 文檔。2006 年 12 月發(fā)布 XML 數(shù)據(jù)通常用于現(xiàn)在的生產(chǎn)系統(tǒng),是數(shù)據(jù)庫實(shí)現(xiàn)的主要部分。過去,這一事實(shí)給數(shù)據(jù)庫開發(fā)人員帶來了設(shè)計(jì)問題,使得他們必須存儲(chǔ)、查詢和更新諸如非結(jié)構(gòu)化 LOB 的 XML 數(shù)據(jù),或者將數(shù)據(jù)“分解”到關(guān)系表中,然后再將其重新組合。該方法導(dǎo)致編程的復(fù)雜且低效,因?yàn)檫@些訪問機(jī)制是不成熟的。 首次隨 Oracle9i 數(shù)據(jù)庫第 2 版推出的 Oracle XML DB 特性提供了用于在數(shù)據(jù)庫中存儲(chǔ)、檢索和操作 XML 數(shù)據(jù)的新功能。然而,Oracle 10g 第 2 版 XML DB 極大地?cái)U(kuò)展了該初始方法,其中 XML 數(shù)據(jù)就像數(shù)據(jù)庫中的 BLOB 一樣。 本文適用于不了解 Oracle 中 XML 實(shí)現(xiàn)的 DBA 或開發(fā)人員:其中簡要概述了 SQL 和 XML 互操作性和如何著手處理存儲(chǔ)在 Oracle 10g 第 2 版數(shù)據(jù)庫中的 XML 文檔,此外,還介紹了 DBA 和開發(fā)人員在數(shù)據(jù)庫中使用 XML 之前應(yīng)該熟悉的由 Oracle 數(shù)據(jù)庫 10g 第 2 版提供的大量新特性。使用這些 SQL/XML 特性,可以輕松增強(qiáng)您在使用關(guān)系數(shù)據(jù)時(shí)獲取的技能,以便幫助您使用 XML。 什么是 Oracle 的 XML 實(shí)現(xiàn)?Oracle 已經(jīng)通過數(shù)據(jù)庫的 XML DB 組件實(shí)現(xiàn)了 XML,該組件是企業(yè)版和標(biāo)準(zhǔn)版的標(biāo)準(zhǔn)特性。通過數(shù)據(jù)庫配置助手 (DBCA) 或者運(yùn)行一個(gè)目錄腳本 catqm.sql,可以輕松安裝 Oracle XML DB。XML DB 產(chǎn)品提供本文討論的所有特性。 要驗(yàn)證是否安裝了 XML DB,只需運(yùn)行下面的 SQL: select comp_name from dba_registry where comp_name like ‘%XML%‘; COMP_NAME Oracle XML Database 也可以查看 XML DB 模式及其對象來確認(rèn)是否安裝了 Oracle XML DB。 SQL 和 XML 協(xié)作SQL/XML 標(biāo)準(zhǔn)已處在開發(fā)之中,以便提供一個(gè)允許我們從關(guān)系查詢生成 XML 的機(jī)制,并反之使我們能夠從 XML 文檔提交 SQL 數(shù)據(jù)。Oracle 數(shù)據(jù)庫 10g 第 2 版中的 Oracle XML DB 實(shí)現(xiàn) SQL 2003 和來自即將推出的 SQL/XML 標(biāo)準(zhǔn)版的特性。Oracle 中這些標(biāo)準(zhǔn)的實(shí)現(xiàn)使我們能夠以在之前 SQL 標(biāo)準(zhǔn)中不可能的方式查看 XML 文檔。 新數(shù)據(jù)類型。Oracle9i 數(shù)據(jù)庫第 1 版中引入了一個(gè)新數(shù)據(jù)類型 XMLType,它允許在 SQL 中訪問數(shù)據(jù)庫中的 XML 文檔,同時(shí)允許 XML 開發(fā)人員在文檔中使用 XML 標(biāo)準(zhǔn)。該數(shù)據(jù)類型通知數(shù)據(jù)庫該內(nèi)容是 XML 格式的,并允許我們在 XML 文檔上執(zhí)行查詢。使用 XMLType(而非一個(gè)關(guān)系或 CLOB 實(shí)現(xiàn))在應(yīng)用程序和存儲(chǔ)模型間提供一個(gè)分離層。該分離允許數(shù)據(jù)移到一個(gè)不同的存儲(chǔ)模型,而不局限于 CLOB 或關(guān)系模型。XMLType 可用于創(chuàng)建表、列或視圖。它還可用作參數(shù)和變量的數(shù)據(jù)類型。 內(nèi)置的 XML 方法可以處理文檔內(nèi)容,允許我們創(chuàng)建、提取和索引 XML 數(shù)據(jù)。索引可以使用 b 樹、文本索引和基于函數(shù)的索引執(zhí)行。實(shí)際上,與 XPath 合并的 XMLType 數(shù)據(jù)可用于查看文檔。該功能通過 PL/SQL 和 Java API 提供。XMLType 可用在 PL/SQL、使用 JDBC 的 Java 以及 Oracle Data Provider for .Net 中。這個(gè)功能強(qiáng)大且相對較新的數(shù)據(jù)類型將在本文中廣泛使用。 存儲(chǔ)結(jié)構(gòu)。Oracle 的 XML 實(shí)現(xiàn)為我們提供了以兩種不同方式存儲(chǔ)數(shù)據(jù)的靈活性:結(jié)構(gòu)化存儲(chǔ)和非結(jié)構(gòu)化存儲(chǔ)。XMLType 數(shù)據(jù)在存儲(chǔ)為單個(gè) LOB 列時(shí)是非結(jié)構(gòu)化的,在實(shí)現(xiàn)為對象集時(shí)是結(jié)構(gòu)化的。 它的一個(gè)特定示例是 XMLType 表,該表可以使用這兩種存儲(chǔ)模型之一實(shí)現(xiàn)。XMLType 表在使用如下所示的“XMLType store as CLOB”語法實(shí)現(xiàn)為 LOB 時(shí)是非結(jié)構(gòu)化的:create table table_name of XMLtype XMLType store as CLOB; 讓我們重點(diǎn)看一下 XML 數(shù)據(jù)的結(jié)構(gòu)化存儲(chǔ)和非結(jié)構(gòu)化存儲(chǔ)并進(jìn)行更詳細(xì)的了解。 結(jié)構(gòu)化存儲(chǔ)。將結(jié)構(gòu)化 XML 存儲(chǔ)實(shí)現(xiàn)為一組對象。這些對象可通過具有可以在表間實(shí)現(xiàn)的引用約束的表以關(guān)系格式實(shí)現(xiàn)。它們也可以使用一個(gè) XMLSchema 實(shí)現(xiàn),以便將一個(gè) XMLType 文檔分解為一組對象。 在使用關(guān)系表的情況中,可以因此對這些表進(jìn)行預(yù)先設(shè)計(jì),或者使用現(xiàn)有表。該文檔實(shí)際上通過關(guān)系表存儲(chǔ)為“虛擬”文檔,并保留其邏輯結(jié)構(gòu)。該方法可維護(hù)文檔對象模型 (DOM) 保真度,但是它不是文檔的逐字節(jié)物理表示。這可以通過創(chuàng)建一個(gè)關(guān)于現(xiàn)有關(guān)系數(shù)據(jù)的 XMLType 視圖來完成。 結(jié)構(gòu)化存儲(chǔ)比非結(jié)構(gòu)化存儲(chǔ)具有一些性能優(yōu)勢,選擇它可通過表和索引設(shè)計(jì)提供更多的查詢和更新優(yōu)化。結(jié)構(gòu)化存儲(chǔ)上的 XML 操作有助于減少內(nèi)存和存儲(chǔ),因?yàn)闆]有存儲(chǔ) XML 標(biāo)記,而且存在更細(xì)粒度的數(shù)據(jù)檢索和使用?;?b 樹和基于函數(shù)的索引使用得到了增強(qiáng),而且使用 XPath 重寫可在文檔的部分上執(zhí)行適當(dāng)更新。所有這些都對性能有所幫助,本文稍后將詳細(xì)討論。 使用結(jié)構(gòu)化存儲(chǔ)也有一些缺點(diǎn)。插入和檢索整個(gè)文檔需要更多開銷。此外,靈活性有限,因?yàn)橹荒艽鎯?chǔ)匹配 XMLSchema 的文檔 — 但是在某些情況中,這實(shí)際上是個(gè)優(yōu)勢。文檔不存儲(chǔ)為逐字節(jié)原始表示,而且不保留文檔中的數(shù)據(jù)順序。但是,這將不會(huì)出現(xiàn)數(shù)據(jù)丟失的情況。 我們通常會(huì)將高度結(jié)構(gòu)化的數(shù)據(jù)看作是 XML,其中數(shù)據(jù)的每個(gè)元素都可以清晰定義。 非結(jié)構(gòu)化存儲(chǔ)。XML 可以使用 CLOB 存儲(chǔ)以非結(jié)構(gòu)化方式存儲(chǔ)在 Oracle 中,以便 SQL 查詢不會(huì)知道該數(shù)據(jù)的結(jié)構(gòu)。該數(shù)據(jù)可以使用 Oracle 的 XMLType 數(shù)據(jù)類型存儲(chǔ)。 如果想讓存儲(chǔ)數(shù)據(jù)匹配文檔的準(zhǔn)確物理表示,可以選擇非結(jié)構(gòu)化數(shù)據(jù)。此外,如果該數(shù)據(jù)不經(jīng)常更新,或者整個(gè)文檔(而非部分文檔)有大量的插入和讀取,則將數(shù)據(jù)整體放在一處可能更高效。在某些情況中,您需要保持文檔的靈活性,而且在這些情況中,XMLType 表或列可能是有用的。 非結(jié)構(gòu)化存儲(chǔ)的缺點(diǎn)是,對部分文檔的更新通常不如結(jié)構(gòu)化數(shù)據(jù)那樣高效。此外,使用函數(shù)從 CLOB 構(gòu)造 DOM 的 XPath 操作將使用大量的系統(tǒng)資源。非結(jié)構(gòu)化數(shù)據(jù)的其他問題是,SQL 約束無法實(shí)現(xiàn),而且內(nèi)存管理也不是如此有效。 我們通常不將非結(jié)構(gòu)化數(shù)據(jù)作為 XML 數(shù)據(jù)考慮,并將其中大部分或所有數(shù)據(jù)只看作是單個(gè) CLOB。 了解了這些存儲(chǔ)模型后,現(xiàn)在讓我們來看一些命名特性。 命名空間中有什么?這是一個(gè)大主題,也是需要了解的較復(fù)雜的 XML DB 概念之一。在本文中,我們將介紹一些命名空間概念,以便您了解其要旨所在及其用法。 命名空間用于描述 XML 文檔中的一組相關(guān)屬性或元素。命名空間可用于確保文檔構(gòu)造有完全唯一的名稱。XMLSchema 尤其要利用該特性,因?yàn)槟繕?biāo)命名空間通常與 XMLSchema 的 URI 相同。命名空間示例如下所示。請注意,該命名看起來類似于一個(gè) Internet URL。該命名標(biāo)準(zhǔn)由萬維網(wǎng)聯(lián)盟 (W3C) 建議,但并非必需。該 URI 用于唯一標(biāo)識(shí)數(shù)據(jù)庫中注冊的 XMLSchema 的名稱,而且無需是文檔所處位置的物理 URL。 XMLType 方法和 XML 函數(shù)使用命名空間前綴。當(dāng) XML 文檔沒有目標(biāo)命名空間時(shí),該命名空間前綴將位于 noNameSpace 命名空間中。屬性 xsi:noNamespaceSchemaLocation 可用于模式 URI。 如果元素不具備另一個(gè)命名空間前綴,默認(rèn)情況下,在將某個(gè)命名空間應(yīng)用到描述它的元素時(shí),可使用該命名空間。 通常您將看到兩個(gè)特定的命名空間。它們是 http://www./2001/XMLSchema(完整的 XMLSchema 命名空間)和 http://xmlns.oracle.com/xdb(Oracle 提供的 XML DB 命名空間)。第二個(gè)命名空間具有寫入基礎(chǔ) SQL 函數(shù)的函數(shù)。一些 XPath 函數(shù)也進(jìn)行重寫。例如 ora:contains、ceiling、floor、not、string-length、substring 和 translate。XML DB 使用的屬性也屬于該命名空間。 定義元素時(shí),我們可以指定該元素的命名空間。在 XML 中,我們可以將它定義為 <elementName xmlns:ab="http.name.com" /> 如您所見,我們提供了一個(gè) elementName,它是我們將使用的元素(例如,“orders”)。Xmlns 是保留字,它告訴我們這是一個(gè)命名空間定義。“ab”部分稱為前綴。該前綴是一個(gè)綁定到 URL 的簡稱。在本例中,ab 綁定到‘http.name.com’,而且 ab 可以加為元素名的前綴。 下面我們將創(chuàng)建一個(gè)非常小的模式,并聲明綁定前綴 xs 到 http://www./2001/XMLSchema 以及聲明前綴 xdb 到 http://xmlns.oracle.com/xdb。為了聲明該命名空間,我們使用 xmlns 綁定它們。請注意,xs 前綴在本例中用在模式元素名稱上。 <xs:schema xmlns:xs="http://www./2001/XMLSchema" xmlns:xdb= "http://xmlns.oracle.com/xdb" version="1.0"> <xs:element name="INVOICESCHEMA" xdb:defaultTable="INVOICESCHEMA"> <xs:complexType> <xs:sequence> <xs:element name="MailAddressTo"> <xs:complexType> <xs:sequence> <xs:element name="Person" type="xs:string"/> <xs:element name="Street" type="xs:string"/> </xs:sequence> <xs:attribute name="id" type="xs:string" use="required"/> </xs:complexType> </xs:element> </xs:sequence> </xs:complexType> </xs:element> </xs:schema> 這里我們只是粗略介紹了一下命名空間。有關(guān)該主題的更多信息,請參閱 Oracle 技術(shù)網(wǎng) (OTN) 上的 Oracle XML DB 開發(fā)人員指南 10g 第 2 版。 使用 XML將數(shù)據(jù)添加到 XMLType 表和列中。正如您可能預(yù)期到的,有很多選項(xiàng)可供我們將 XML 數(shù)據(jù)添加到數(shù)據(jù)庫中??梢允褂?SQL、PL/SQL、Java 和 C 程序插入數(shù)據(jù)。也可以使用 XMLType 表的 SQL*Loader 和 Import/Export 函數(shù)將數(shù)據(jù)加載到數(shù)據(jù)庫中??梢允褂?Oracle Streams 來移動(dòng) XML 數(shù)據(jù),還可以使用 DBMS_XDB 函數(shù)。現(xiàn)在,就讓我們來探究一個(gè)簡單易用的方式來加載 XML 數(shù)據(jù)。 在本例中,我們將使用 SQL 插入將一個(gè) XML 文檔從文件加載到表中,以便從 bfile 加載該 XML 文檔。該文件需要通過一個(gè)數(shù)據(jù)庫目錄訪問。為此,我們將創(chuàng)建一個(gè)目錄以指向存儲(chǔ)該 XML 文檔文件的文件。請注意,要執(zhí)行本文中的示例,您必須確保所用用戶具有 xdbadmin 角色。 create or replace directory xmldir as ‘/u01/app/oracle/admin/db01/sql‘; 現(xiàn)在我們可以用兩種方式創(chuàng)建包含 XMLType 數(shù)據(jù)的表。第一個(gè)將是具有 XMLType 列的表。第二個(gè)將是定義為類型 XMLType 的表。 1. 創(chuàng)建一個(gè)包含 XML 列的表。 create table invoiceXML_col ( inv_id number primary key, inv_doc XMLType); 2. 創(chuàng)建一個(gè) XML 表。 create table invoiceXML_tbl of XMLtype; 以上每一個(gè)語句都在每個(gè)表中隱式創(chuàng)建兩個(gè)索引 — 一個(gè)用于主鍵,另一個(gè)用于 LOB。 現(xiàn)在我們要將一個(gè)發(fā)票文檔插入到這兩個(gè)表中。請注意,我們要使用字符集 AL32UTF8,并指定它傳送字符集編碼來供要讀取的文件使用。數(shù)據(jù)將插入這兩個(gè)表以顯示這兩個(gè) SQL 語句間的相似性。 Insert into invoicexml_col values (1, XMLType(bfilename(‘XMLDIR‘, ‘invoicexml.txt‘), nls_charset_id(‘AL32UTF8‘) )); Insert into invoicexml_tbl values (XMLType(bfilename(‘XMLDIR‘, ‘invoicexml.txt‘), nls_charset_id(‘AL32UTF8‘))); 我們現(xiàn)在已經(jīng)使用 XMLType 表和列將兩個(gè) XML 文檔加載到了數(shù)據(jù)庫中。就是這么簡單。 注冊和實(shí)現(xiàn)模式類型化數(shù)據(jù)如果處理命名空間和存儲(chǔ)技術(shù)不足,XMLType 數(shù)據(jù)也可以實(shí)現(xiàn)為模式類型化或非類型化數(shù)據(jù)。XMLSchema 可將文檔與提供文檔結(jié)構(gòu)和內(nèi)容信息的 XMLSchema 相關(guān)聯(lián)。這為文檔提供了更好的記錄、驗(yàn)證和控制。該模式中的信息可用于提交更有效的查詢和更新處理。XMLSchema 也允許將 XML 文檔存儲(chǔ)為結(jié)構(gòu)化存儲(chǔ)數(shù)據(jù),因?yàn)樵撐臋n可以通過 XMLSchema 分解為一組對象。用于存儲(chǔ)該文檔的對象模型由模式定義確定。XMLType 方法 schemaValidate() 和 isSchemaValid() 允許使用模式定義編輯 XMLType 文檔。 我們?nèi)绾蝿?chuàng)建模式并在 XML 文檔中使用它?為此,我們需要?jiǎng)?chuàng)建一個(gè) XMLSchema 定義
下面的示例分四步進(jìn)行了說明。 1. 首先,在目錄 XML_DIR 下的文件 invoiceformtest.xsd 中創(chuàng)建一個(gè)模式定義。該清單如下所示: <xs:schema xmlns:xs="http://www./2001/XMLSchema" xmlns:xdb=http://xmlns.oracle.com/xdb version="1.0"> <xs:element name="INVOICEFORMTEST" xdb:defaultTable="INVOICEFORMTEST"> <xs:complexType> <xs:sequence> <xs:element name="MailAddressTo"> <xs:complexType> <xs:sequence> <xs:element name="Person" type="xs:string"/> <xs:element name="Street" type="xs:string"/> <xs:element name="City" type="xs:string"/> <xs:element name="State" type="xs:string"/> <xs:element name="Zipcode" type="xs:string"/> </xs:sequence> <xs:attribute name="id" type="xs:string" use="required"/> </xs:element> </xs:sequence> </xs:complexType> </xs:element> </xs:schema> 2. 使用過程 DBMS_XMLSCHEMA.registerSchema 在數(shù)據(jù)庫中注冊上面的模式。您用來連接到數(shù)據(jù)庫的用戶名需要修改會(huì)話權(quán)限,以便該模式可以成功注冊。 BEGIN DBMS_XMLSCHEMA.registerSchema( SCHEMAURL => ‘http://xmlns.oracle.com/xdb/invoiceformtest.xsd‘, SCHEMADOC => bfilename(‘XMLDIR‘,‘invoiceformtest.xsd‘), CSID => nls_charset_id(‘AL32UTF8‘)); END; / 要?jiǎng)h除該模式,只需運(yùn)行 DBMS_XMLSCHEMA.deleteSchema 語句,如下所示: BEGIN DBMS_XMLSCHEMA.deleteSchema( SCHEMAURL => ‘http://xmlns.oracle.com/xdb/invoiceformtest.xsd‘, DELETE_OPTION => dbms_xmlschema.DELETE_CASCADE_FORCE); END; / 3. 由于上面模式定義中的 xdb:defaultTable="INVOICEFORMTEST" 語句,以上語句還創(chuàng)建了表 invoiceformtest。沒有它,將創(chuàng)建一個(gè)生成的名稱(您不想使用該名稱)。 4. 現(xiàn)在,我們準(zhǔn)備將文檔輸入到 XML 文檔內(nèi)基于 XMLSchema 的表中,該文檔存儲(chǔ)在 XML_DIR 中并使用實(shí)現(xiàn)的模式指定的格式。 Insert into invoiceformtest values (XMLType(bfilename(‘XMLDIR‘, ‘invoiceformtest.txt‘), nls_charset_id(‘ AL32UTF8‘))); 來看一下我們剛剛使用 XML 文檔上的 object_value pseudocolumn 創(chuàng)建的內(nèi)容。 SQL> select object_value from invoiceformtest; OBJECT_VALUE <INVOICEFORMTEST> <MailAddressTo id="1"> <Person>Joe Smith</Person> <Street>10 Apple Tree Lane</Street> <City>New York</City> <State>NY</State> <Zipcode>12345</Zipcode> </MailAddressTo> </INVOICEFORMTEST> 完成! 了解 XQuery 函數(shù)和表達(dá)式XQuery 是 W3C 針對查詢語言開發(fā)的一個(gè)標(biāo)準(zhǔn),用于從 XML 文件提取信息。它允許我們訪問物理 XML 文檔或者通過 XML 視圖虛擬化為 XML 文檔的關(guān)系數(shù)據(jù)。使用的表達(dá)式可以是簡單的查詢或較大查詢的一部分,而且可以包括標(biāo)準(zhǔn)函數(shù),例如,日期/時(shí)間、算術(shù)或字符串函數(shù)以及用戶定義的函數(shù)。XQuery 是 Oracle 數(shù)據(jù)庫 10g 第 2 版中的新特性,并受 XMLQuery() 和 XMLTable() 函數(shù)支持。使用這兩個(gè)函數(shù)的示例稍后在“XMLQuery() 和 XMLTable()”部分中有述。 XQuery 使用 XPath 表達(dá)式(如下所述)定位 XML 文檔中的詳細(xì)項(xiàng)。可將其看作是 Oracle 數(shù)據(jù)庫 10g 第 2 版中引入的 XMLQuery() 和 XMLTable() 函數(shù)的基礎(chǔ),本文稍后有述。 以下將介紹一些更重要的 XQuery 表達(dá)式(包括 XPath、XMLSequence 和 FLWOR)。 XPath、XMLSequence 和 FLWOR 表達(dá)式XPath。XPath 提供使用定址技術(shù)在 XML 文檔中定位項(xiàng)的方式,而且它通過一個(gè)文檔結(jié)構(gòu)使用邏輯路徑處理這些項(xiàng)。它允許程序員在較高的抽象級別處理文檔,方法是通過文檔指定路由而非指向特定元素。XPath 使用節(jié)點(diǎn)概念定義路徑在何處開始,然后定義一個(gè)“邏輯樹”,該樹包括諸如 attribute、self、parent、child 和 ancestor 等關(guān)系。換言之,XPath 會(huì)將 XML 文檔建模為節(jié)點(diǎn)樹。節(jié)點(diǎn)類型分為若干種,例如,元素節(jié)點(diǎn)、屬性節(jié)點(diǎn)和文本節(jié)點(diǎn)。XPath 可以確定一個(gè)方式來為每個(gè)節(jié)點(diǎn)計(jì)算字符串值。 憑借 XPath 表達(dá)式,您可使用基于標(biāo)準(zhǔn)的方式查詢和更新 XML 文檔。我們將使用 extract、extractValue、existsNode 和 XMLSequence 函數(shù)來演示 XPath 功能,期間將用到我們已創(chuàng)建的 invoicexml_tbl 文檔以及插入到其中的數(shù)據(jù)。 查詢要在 XPath 示例中使用的文檔。讓我們看一下如何使用 XPath 函數(shù)通過 SQL 搜索 XML (XMLType) 數(shù)據(jù)。為此,來看一下要使用 object_value pseudocolumn 從 XMLType 表檢索 XML 文檔時(shí)涉及的文檔。 SQL> select object_value from invoicexml_tbl; OBJECT_VALUE <Invoice> <MailAddressTo id="PA"> <Person>Joe Smith</Person> <Street>10 Apple Tree Lane</Street> <City>New York</City> <State>NY</State> <Zipcode>12345</Zipcode> </MailAddressTo> <MailAddressFrom id="PA"> <Person>Ed Jones</Person> <Street>11 Cherry Lane</Street> <City>Newark</City> <State>NJ</State> <Zipcode>67890</Zipcode> </MailAddressFrom> <Details id="2006Sept1to30PA"> <FromTo>Sept 1, 2006 to Sept 30, 2006</FromTo> <Hours>70</Hours> <Rate>30</Rate> <Taxes>210</Taxes> <TotalDue>2310</TotalDue> <InvDate>Oct 1, 2006</InvDate> <Contractor>Ed Jones</Contractor> </Details> </Invoice> 使用 extract。使用 extract 時(shí),通過將 extract 與 object_value 合并,可從文檔選擇單個(gè)節(jié)點(diǎn)及其葉節(jié)點(diǎn)。換言之,我們可以查看存儲(chǔ)為 XMLType 的 XML 文檔。無論我們使用的是結(jié)構(gòu)化數(shù)據(jù)還是非結(jié)構(gòu)化數(shù)據(jù)以及數(shù)據(jù)是否基于模式,情況都是如此。開始提取 MailAddressTo 節(jié)點(diǎn)及其葉節(jié)點(diǎn)。 select extract(object_value, ‘/Invoice/MailAddressTo‘) from invoicexml_tbl; EXTRACT(OBJECT_VALUE,‘/INVOICE/MAILADDRESSTO‘) <MailAddressTo id="PA"><Person>Joe Smith</Person><Street>10 Apple Tree Lane</Street><City>New York</City><State>NY</Stat e><Zipcode>12345</Zipcode></MailAddressTo> 如您所見,該輸出包括文檔的 MailAddressTo 部分,不適合打?。ǜ袷交4送?,用于實(shí)現(xiàn)它的語法非常簡單。重要的是,我們能夠在無需轉(zhuǎn)儲(chǔ)整個(gè)內(nèi)容的情況下查看文檔。 使用 extractValue。葉節(jié)點(diǎn)中存在的數(shù)據(jù)值可以使用 extractValue 提取。較高級的節(jié)點(diǎn)(例如 MailAddressTo)無法使用該函數(shù)提取。請注意,它的輸出不是 XML 語法格式,其中只包含該數(shù)據(jù)值。 select extractValue(object_value, ‘/Invoice/MailAddressTo/Person‘) Person from invoicexml_tbl; PERSON Joe Smith 使用 existsNode。ExistsNode 以類似的方式使用,以便在節(jié)點(diǎn)級(只能在節(jié)點(diǎn)級)搜索文檔的特定值。它返回一個(gè) True 或 False 標(biāo)志來指定搜索是否成功。= 1 謂詞不是次數(shù),表示的是 True 條件,而 = 0 表示 False。 Select count(*) from invoicexml_tbl where existsNode( object_value, ‘/Invoice/MailAddressTo[Person="Joe Smith"]‘) = 1; COUNT(*) 1 使用 XMLSequence。與 extractValue(只可從單個(gè)節(jié)點(diǎn)提取的值)不同,XMLSequence 可用于查看文檔的多個(gè)節(jié)點(diǎn)或某個(gè)片段。它通過創(chuàng)建包含 XMLType 對象的虛擬表來完成此操作。讓我們使用 MailAddressTo 分支節(jié)點(diǎn)來對 extractValue 和 XMLSequence 進(jìn)行比較。 select extractValue(object_value, ‘/Invoice/MailAddressTo‘) from invoicexml_tbl; from invoicexml_tbl * ERROR at line 2: ORA-19025: EXTRACTVALUE returns value of only one node 該 ORA-19025 消息具有自我說明性。幸運(yùn)的是,我們可以通過重構(gòu)該查詢并使用 XMLSequence 解決該問題,如下所示: select value(addr) from invoicexml_tbl i, table(XMLSequence( extract(i.object_value, ‘/Invoice/MailAddressTo‘))) addr where existsNode(i.object_value, ‘/Invoice/Details[@id="2006Sept1to30PA"]‘) = 1; VALUE(ADDR) <MailAddressTo id="PA"><Person>Joe Smith</Person><Street>10 Apple Tree Lane</Street><City>New York</City><State>NY</Stat e><Zipcode>12345</Zipcode></MailAddressTo> 由于 XMLSequence 創(chuàng)建一個(gè)虛擬表,我們也可以在葉節(jié)點(diǎn)上使用該函數(shù)。 select value(person) from invoicexml_tbl i, table(XMLSequence( extract(i.object_value, ‘/Invoice/MailAddressTo/Person‘))) person where existsnode(i.object_value, ‘/Invoice/Details[@id="2006Sept1to30PA"]‘) = 1; VALUE(PERSON) <Person>Joe Smith</Person> FLWOR。FOR、LET、WHERE、ORDER BY 和 RETURN(FLWOR;發(fā)音為“flower”)是 XQuery 語法中最重要且最強(qiáng)大的表達(dá)式之一。FOR 或 LET 必須存在,WHERE 和 ORDER BY 是可選的,而 RETURN 是必需的。FLWOR 本身就是一個(gè)有待介紹的大主題。本節(jié)將為您簡要介紹一下該語句的強(qiáng)大功能。 FOR 通過迭代方式以變量的列出順序綁定一個(gè)或多個(gè)變量。前面列出的值之后可用在一組后續(xù)的值中。這種工作方式也類似于 SQL From 子句。前面列出的值之后可用在一組后續(xù)的值中,如下所示: For $var in (1,2,3) , $varPlus5 in (5+$var, 5+$var, 5+$var) 這三個(gè)迭代將 $var 和 $varPlus5 分別設(shè)置為 1,6;2,7 和 3,8。 LET(類似于 FOR)以迭代方式綁定變量,可以使用以前計(jì)算的值來計(jì)算值。與使用 FOR 一樣,可將 LET 看作 SQL FROM 字句。LET 也可以用于執(zhí)行連接。 WHERE 過濾數(shù)據(jù)的方式與 SQL WHERE 子句相同。 ORDER BY 可以隨意對數(shù)據(jù)進(jìn)行排序。 RETURN 從過濾和排序后的 FLWOR 表達(dá)式返回最終結(jié)果集。 FLWOR 與 XMLQuery() 協(xié)作。以下是查詢和連接兩個(gè)文檔的示例:用 Party 鍵將 partys.xml 連接到 orders.xml 文檔。該 XML 數(shù)據(jù)位于 Oracle XML DB 信息庫中。為此,我們將使用 XMLQuery();FLWOR;以及 XQuery 函數(shù) doc、count、avg 和 integer。它們位于內(nèi)置 XQuery 函數(shù)的命名空間中,http://www./2003/11/xpath_functions。 以下查詢讀取結(jié)果如下所示:使用函數(shù) fn:doc,對于 (FOR) partys.xml 中的所有 partyno 屬性,連接 (LET) orders.xml 中匹配 partyno 的所有定單元素(變量 $p 綁定在 FOR 語句中)。這將生成一個(gè)項(xiàng)流($p 和 $o),其中 $p 表示一方的編號(hào),而 $o 表示該方的一組定單。獲取其中 (WHERE) 有多個(gè)定單的項(xiàng)。使用命名空間 fn 中的 XQuery 函數(shù) avg 對平均數(shù) amt 進(jìn)行降序排序 (ORDER BY)。 Amt 附加到定單元素 $o。返回該方的編號(hào)(綁定到 $p)和子元素定單計(jì)數(shù)。 SELECT XMLQuery()(‘for $p in fn:doc("/public/partys.xml")/partys/party/@partyno let $o := fn:doc("/public/orders.xml")/orders/order[@partyno = $p] where fn:count($o) > 1 order by fn:avg($o/@amt) descending return <big-party>{$p, <ordercount>{fn:count($o)}</ordercount>, <avgamt>{xs:integer(fn:avg($o/@amt))}</avgamt>} </big-party>‘ RETURNING CONTENT) ORDERS FROM DUAL; ORDERS <big-party>1111<ordercount>2</ordercount><avgamt>3500</avgamt></big-party> 是的,該查詢正在進(jìn)行大量工作并在 XML 文檔上顯示使用 FLWOR 表達(dá)式時(shí)可能出現(xiàn)的結(jié)果。 XQuery ora:函數(shù)Oracle XML DB 提供五個(gè)開發(fā)人員可以使用的 XQuery 函數(shù)。它們在 http://xmlns.oracle.com/xdb 命名空間中實(shí)現(xiàn),該命名空間使用前綴 ora:。除此之外還有另外兩個(gè) XPath 函數(shù)。它們?nèi)匀徊皇?SQL/XML 標(biāo)準(zhǔn)的一部分,但預(yù)期將來會(huì)是。該 ora:view 函數(shù)是一個(gè)特別有價(jià)值的函數(shù),可用于將關(guān)系數(shù)據(jù)轉(zhuǎn)換為 XML 格式。下面將對所有這些函數(shù)進(jìn)行介紹。 ora:view XQuery 函數(shù)。這是在關(guān)系表上創(chuàng)建視圖的有用函數(shù)。這可以使它們看起來像 XML 文檔。 Syntax: ora:view ([db-schema STRING, ] db_table STRING) RETURNS document-node (element()) 本文稍后將給出使用 ora:view 的示例。 ora:contains XQuery 函數(shù)。命名空間前綴“ora:”和名稱“contains”一起使用可創(chuàng)建 XPath 函數(shù),該函數(shù)可用作 SQL/XML existsNode、extract 和 extractValue 函數(shù)的 XPath 參數(shù)。這使您能夠使用文本謂詞執(zhí)行結(jié)構(gòu)搜索。 如果 text_query 與 input_text 匹配,該函數(shù)將返回一個(gè)正數(shù)。如果它們不匹配,則返回 0。當(dāng)它們與 existsNode、extract 或 extractValue 一起使用時(shí),您需要包括命名空間映射參數(shù) xmlns:ora="http://xmlns.oracle.com/xdb"。ora:contains 的語法如下所示。當(dāng)未定義 policy_owner 時(shí),所有者是當(dāng)前用戶。policy_name 是要應(yīng)用的匹配規(guī)則的名稱。默認(rèn)的匹配規(guī)則由 ctxsys.default_policy_oracontains 定義。 Syntax: ora:contains (input_text, text_query [, policy_name] [, policy_owner]) 下面的示例顯示了如何使用 ora:contains 作為 existsNode 的參數(shù)。 SELECT count(*) FROM invoicexml_tbl WHERE existsNode(object_value, ‘/Invoice/MailAddressTo/Person [ora:contains(text(), "Joe Smith") > 0]‘, ‘xmlns:ora="http://xmlns.oracle.com/xdb"‘) = 1; COUNT(*) 1 ora:matches XQuery 函數(shù)。該函數(shù)使用正則表達(dá)式匹配文本。這類似于 SQL REGEXP_LIKE 條件,但是它使用 XQuery 參數(shù)而非 SQL 數(shù)據(jù)類型。如果 target_string 匹配正則表達(dá)式 match_pattern,則返回 True;否則,返回 False。正如您在下面語法中所見,可以添加 match_parameter 以指定在搜索中使用的附加標(biāo)準(zhǔn)。以下示例是提供區(qū)分大小寫。 Syntax: ora:matches (target_string, match_pattern [, match_parameter]) ora:replace XQuery 函數(shù)。該函數(shù)在滿足 match_pattern 的情況下 (True) 使用 replace_string 替換 target_string,這是對 ora:matches 函數(shù)的擴(kuò)展。與使用 ora:matches 一樣,使用正則表達(dá)式來匹配文本。 Syntax: ora:replace (target_string, match_pattern, replace_string [, match_parameter]) ora:sqrt XQuery 函數(shù)。顧名思義,該函數(shù)返回提供數(shù)字的平方根值。 Syntax: ora:sqrt (number) XPath 擴(kuò)展函數(shù):ora:instanceof 和 ora:instanceof-only。當(dāng)知道屬性和元素的數(shù)據(jù)類型時(shí),Oracle XML DB 可以支持基于模式的數(shù)據(jù)。由于 XPath 1.0 不知道數(shù)據(jù)類型信息,因此在命名空間 http://xmlns.oracle.com/xdb 中有 XML DB 擴(kuò)展函數(shù),它們使您能夠?qū)?XML 文檔節(jié)點(diǎn)限制為特定數(shù)據(jù)類型。函數(shù) ora:instanceof 可用于將節(jié)點(diǎn)限制為數(shù)據(jù)類型或子類型,而 ora:instanceof-only 只將節(jié)點(diǎn)限制為數(shù)據(jù)類型。子類型是用于擴(kuò)展或限制類型的特性。 Syntax: ora:instanceof(nodeset-expr, typename [, schema-url]) Syntax: ora:instanceof-only(nodeset-expr, typename [, schema-url]) 在上面的語法中,nodeset-expr 通常是一個(gè)相關(guān)的 XPath 表達(dá)式。如果任何節(jié)點(diǎn)的數(shù)據(jù)類型與類型名匹配,則該函數(shù)返回 True,否則返回 False。類型名可使用命名空間前綴限定。 對于非基于模式的數(shù)據(jù),這些函數(shù)將返回 False,因?yàn)檫@些數(shù)據(jù)沒有與元素和屬性相關(guān)的數(shù)據(jù)類型。 使用 fn:doc 和 fn:collection 函數(shù)在 XML DB 信息庫中查詢 XML 數(shù)據(jù)有兩個(gè)重要的 XQuery 函數(shù)可用于在 XML DB 信息庫中查詢所有資源。Fn:doc 是一個(gè) XQuery 函數(shù),可以獲取包含 XML 數(shù)據(jù)的信息庫文件。該文件資源由其 URL 參數(shù)指出。XQuery 變量可以使用 FLWOR 表達(dá)式 FOR 和 LET 綁定到數(shù)據(jù)。XQuery 函數(shù) fn:doc 可用于讀取存儲(chǔ)在 XML DB 信息庫中的單個(gè) XML 文檔。 在信息庫中查詢資源的第二個(gè) XQuery 函數(shù)是 fn:collection。該函數(shù)可以返回存儲(chǔ)在信息庫中同一文件夾中的大量類似文檔。 現(xiàn)在,我們將通過創(chuàng)建信息庫資源來演示 fn:doc 和 fn:collection 如何處理一些簡單示例。首先,使用 DBMS_XDB PL/SQL 程序包創(chuàng)建一個(gè)資源。這可用于在 XML DB 中管理資源。該 createResource 過程在本例中用于創(chuàng)建包含 Orders 和 Partys XML 文檔的新文件資源。 創(chuàng)建用于以下示例的定單和定方資源名。其中有一個(gè)名為 ordersnamespace.xml 的附加資源,它是一個(gè)使用命名空間的示例。 DECLARE res BOOLEAN; ordersxmlstring VARCHAR2(500):= ‘<?xml version="1.0"?> <orders> <order orderno="15" partyno="1111" itemname="Widget" amt="5000"/> <order orderno="25" partyno="1111" itemname="Do dad" amt="2000"/> <order orderno="35" partyno="2222" itemname="All purpose item" amt="7000"/> <order orderno="45" partyno="3333" itemname="The best thing" amt="15000"/> </orders>‘; partysxmlstring VARCHAR2(500):= ‘<?xml version="1.0"?> <partys> <party partyno="1111" partyname="ABC Corp" partycity="Toronto"/> <party partyno="2222" partyname="Freds Inc" partycity="Chicago"/> <party partyno="3333" partyname="Gofaster Corp" partycity="Montreal"/> </partys>‘; ordersxmlnsstring VARCHAR2(500):= ‘<?xml version="1.0"?> <orders xmlns="http://"> <order orderno="15" partyno="1111" itemname="Widget" amt="5000"/> <order orderno="25" partyno="1111" itemname="Do dad" amt="2000"/> <order orderno="35" partyno="2222" itemname="All purpose item" amt="7000"/> <order orderno="45" partyno="3333" itemname="The best thing" amt="15000"/> </orders>‘; BEGIN res := DBMS_XDB.createResource(‘/public/orders.xml‘, ordersxmlstring); res := DBMS_XDB.createResource(‘/public/partys.xml‘, partysxmlstring); res := DBMS_XDB.createResource(‘/public/ordersnamespace.xml‘, ordersxmlnsstring); END; 我們可以看到剛剛通過查詢 resource_view 創(chuàng)建的資源,如下所示: SQL> select any_path, res from resource_view where any_path like ‘%partys%‘; ANY_PATH RES /public/partys.xml <Resource xmlns="http://xmlns.oracle.com/xdb/XDBResource.xsd"> <CreationDate>2006-06-19T17:12:00.414000</CreationDate> <ModificationDate>2006-06-19T17:12:00.414000</ModificationDate> <DisplayName>partys.xml</DisplayName> <Language>en-US</Language> <CharacterSet>WINDOWS-1252</CharacterSet> <ContentType>text/xml</ContentType> <RefCount>1</RefCount> </Resource> 接下來,使用 fn:doc 顯示單個(gè) partys.xml 文檔。 SELECT XMLQuery(‘for $p in fn:doc("/public/partys.xml") return $p‘ RETURNING CONTENT) partys FROM DUAL; PARTYS --------------------------------------------------------------------- <partys><party partyno="1111" partyname="ABC Corp" partycity="Toronto"/><party partyno="2222" partyname="Freds Inc" partycity="Chicago"/> <party partyno="3333" partyname="Gofaster Corp" partycity="Montreal"/></partys> 對 Fn:collection 的使用與 fn:doc 稍有不同,它可返回存儲(chǔ)在信息庫中同一文件夾中的所有文檔。從該語句生成的輸出不適合打印,手動(dòng)進(jìn)行以下格式化以使該文檔可讀性更高。 SELECT XMLQuery(‘for $p in fn:collection("/public") return $p‘ RETURNING CONTENT) collection_public FROM DUAL; COLLECTION_PUBLIC ---------------------------------------------------------------------- <orders> <order orderno="15" partyno="1111" itemname="Widget" amt="5000"/> <order orderno="25" partyno="1111" itemname="Do dad" amt="2000"/> <order orderno="35" partyno="2222" itemname="All purpose item" amt="7000"/> <order orderno="45" partyno="3333" itemname="The best thing" amt="15000"/> </orders> <orders xmlns="http://"> <order orderno="15" partyno="1111" itemname="Widget" amt="5000"/> <order orderno="25" partyno="1111" itemname="Do dad" amt="2000"/> <order orderno="35" partyno="2222" itemname="All purpose item" amt="7000"/> <order orderno="45" partyno="3333" itemname="The best thing" amt="15000"/> </orders> <partys> <party partyno="1111" partyname="ABC Corp" partycity="Toronto"/> <party partyno="2222" partyname="Freds Inc" partycity="Chicago"/> <party partyno="3333" partyname="Gofaster Corp" partycity="Montreal"/> </partys> 還有其他大量 DBMS_XDB 過程和函數(shù)可用于管理所有 XML DB 資源,例如,提供刪除資源和創(chuàng)建文件夾的功能。這些函數(shù)可在 OTN 上的 Oracle XML DB 開發(fā)人員指南 10g 第 2 版 中找到。 XMLQuery() 和 XMLTable()Oracle 數(shù)據(jù)庫 10g 第 2 版中引入了 XMLQuery() 和 XMLTable() 函數(shù)。它們在 SQL 和 XML 之間提供了強(qiáng)大的接口,允許我們查詢、構(gòu)造和轉(zhuǎn)換關(guān)系數(shù)據(jù),就像它是 XML,反之也可以將 XML 數(shù)據(jù)當(dāng)作關(guān)系數(shù)據(jù)來處理。通常,我們使用 XMLQuery() 從關(guān)系數(shù)據(jù)生成 XML 文檔,并使用 XMLTable() 從 XML 數(shù)據(jù)創(chuàng)建關(guān)系視圖。它們?nèi)匀徊皇?ANSISQL 的一部分,但預(yù)計(jì)會(huì)包括在將來的標(biāo)準(zhǔn)中。 XMLQuery() 函數(shù)。該函數(shù)用于構(gòu)造 XML 數(shù)據(jù)并使用 XQuery 來查詢該數(shù)據(jù)。它支持在 SQL 上下文中執(zhí)行 XQuery 表達(dá)式。我們也可以通過使用 ora:view 為關(guān)系數(shù)據(jù)創(chuàng)建動(dòng)態(tài) XML 視圖來查詢關(guān)系數(shù)據(jù),并能夠操作部分 XML 文檔而非整個(gè)文檔。 XMLQuery() 將 XQuery 表達(dá)式作為字符串文字,將可選的 XQuery“上下文項(xiàng)”作為 SQL 表達(dá)式。該表達(dá)式設(shè)置將在其中評估 XQuery 表達(dá)式的 XPath 上下文。該函數(shù)將 XQuery 表達(dá)式結(jié)果作為 XMLType 實(shí)例返回。XMLQuery() 函數(shù)也可以將 SQL 表達(dá)式作為參數(shù),其中當(dāng)評估表達(dá)式時(shí),值綁定到 XQuery 變量。這些結(jié)果作為 XMLType 實(shí)例返回。 來看兩個(gè)示例,其中我們針對關(guān)系數(shù)據(jù)和 XML 數(shù)據(jù)使用了 XMLQuery()。 • 將 XMLQuery() 與 ora:view 和 FLWOR 表達(dá)式合并的示例。在第一個(gè)示例中,我們將在物理關(guān)系表上使用 XMLQuery()。XMLQuery() 必須操作 XML 數(shù)據(jù),這可通過使用 HR 模式中關(guān)系表 Employee 和 Department 上的 ora:view 特性完成。當(dāng) ora:view 用在這兩個(gè)關(guān)系表上之后,它們將顯示為 XML,然后我們就能夠使用 XQuery 表達(dá)式,包括嵌套的 FLWOR 表達(dá)式。 下面的查詢正在進(jìn)行以下操作:對于每個(gè)部門,獲取部門 id,對于匹配部門 id 且傭金高于 30% 的雇員,返回雇員的名和姓。請注意,F(xiàn)OR 表達(dá)式在該查詢中使用了兩次。 SELECT XMLQuery( ‘FOR $dep in ora:view("DEPARTMENTS")/ROW RETURN <Department id="{$dep/DEPARTMENT_ID}"> <Employee> {FOR $emp in ora:view("EMPLOYEES")/ROW WHERE $emp/DEPARTMENT_ID eq $dep/DEPARTMENT_ID and $emp/COMMISSION_PCT > .3 RETURN ($emp/FIRST_NAME, $emp/LAST_NAME)} </Employee> </Department>‘ RETURNING CONTENT) HIGH_COMMISSION_EMP_NAMES FROM DUAL; HIGH_COMMISSION_EMP_NAMES --------------------------------------------------------------- <Department id="10"><Employee></Employee></Department><Department id="20"><Employee></Employee></Department><Department id="30"><Employee></Employee></Department><Department id="40"><Employee></Employee></Department><Department id="50"><Employee></Employee></Department><Department id="60"><Employee></Employee></Department> 正如您所見,結(jié)果不適合打印。該查詢語法有點(diǎn)復(fù)雜,但它顯示出我們能夠使用 ora:view 將關(guān)系數(shù)據(jù)轉(zhuǎn)換為 XML,并能夠針對 XML 文檔執(zhí)行連接操作以及將謂詞應(yīng)用到文檔中的數(shù)據(jù)上。 • 將 XMLQuery() 與 XMLType 列和 FLWOR 表達(dá)式一起使用的示例。表 invoicexml_col(我們在上面創(chuàng)建的,并在其中插入了一個(gè)文檔)包含一個(gè) XMLType 列 inv_doc。存儲(chǔ)在該列中的 Invoice 數(shù)據(jù)是 XML 格式的。在該查詢中,我們使用具有 PASSING 子句的 XMLQuery() 函數(shù)將 XMLType 列 inv_doc 傳遞到 XQuery。請注意我們是如何從基于 WHERE 謂詞的 Invoice 文檔返回一些特定字段的。實(shí)際上,我們能夠進(jìn)入 XML 文檔并根據(jù)謂詞檢索特定字段,而非只能夠?qū)⒋宋臋n作為 CLOB 查看。 下面的 SELECT 語句適用于 invoicexml_col 的所有行。然后我們可以使用 FOR 語句迭代所有發(fā)票行。WHERE 謂詞取出 Zipcode 12345 的元素,然后返回 City、State 和 Zipcode,以及是否為 Zipcode 輸入了正確的 City 和 State。 IF…THEN…ELSE 構(gòu)造將體現(xiàn)該語法的一些附加功能。 Select XMLQuery( ‘FOR $i in /Invoice WHERE $i/MailAddressTo/Zipcode = 12345 RETURN <Details> <Zipcode num="{$i/MailAddressTo/Zipcode}"/> <CityName char="{$i/MailAddressTo/City}"/> <City>{IF ($i/MailAddressTo/City = "New York") THEN "Correct City" ELSE "Incorrect City"} </City> <State>{if ($i/MailAddressTo/State = "NY") then "Correct State" else "Incorrect State"} </State> </Details>‘ PASSING inv_doc RETURNING CONTENT) ny_invoice FROM invoicexml_col; NY_INVOICE <Details><Zipcode num="12345"></Zipcode> <CityName char="New York"></CityName> <City>Correct City</City> <State>Correct State</State> </Details> 這兩個(gè)查詢闡釋了我們必須以詳細(xì)的分段方式查看和處理 XML 文檔的功能。提供的功能類似于我們用關(guān)系數(shù)據(jù)進(jìn)行的操作 — 令人驚奇! XMLTable() 函數(shù)。該函數(shù)支持將 XML 值解釋為表或集。它用于從 XQuery 表達(dá)式的評估返回表和列,而不是像通常那樣返回一個(gè)序列作為 XQuery??梢圆樵?XMLType 數(shù)據(jù)并將 XML 結(jié)果分割或分解為關(guān)系格式 — 可將其看作是創(chuàng)建一個(gè)虛擬表。然后,可以使用該虛擬表將數(shù)據(jù)插入到其他表中,或者查詢該虛擬表。關(guān)系視圖也可以針對 XML 數(shù)據(jù)進(jìn)行構(gòu)造。XMLTable() 函數(shù)也可用在 SQL From 子句中。 • 將 XMLTable() 與 COLUMNS 子句一起使用的示例。我們將使用前面介紹過 invoicexml_col 表來闡釋如何使用 XMLTable() 將 XML 數(shù)據(jù)轉(zhuǎn)換為關(guān)系格式。在下面的示例中,XMLTable() 訪問存儲(chǔ)在列 inv_doc 中的 Invoice 文檔。使用 COLUMNS 子句將所需數(shù)據(jù)元素的路徑映射到新的名稱和格式。XMLTable() 函數(shù)返回?cái)?shù)據(jù)作為虛擬表,該查詢的結(jié)果與我們已經(jīng)查詢過的關(guān)系表一樣。請注意,通過在該查詢底部使用的 WHERE 子句,我們可以過濾 XML 數(shù)據(jù),過濾的方式與使用任何為關(guān)系數(shù)據(jù)編寫的 SQL 查詢的方式完全相同。 該查詢和輸出如下所示: SELECT inv_id, a.PersonName, a.StreetName, a.CityName, a.State, a.Zipcode FROM invoicexml_col, XMLTABLE(‘/Invoice‘ PASSING invoicexml_col.inv_doc COLUMNS PersonName varchar2(10) PATH ‘/Invoice/MailAddressTo/Person‘, StreetName varchar2(20) PATH ‘/Invoice/MailAddressTo/Street‘, CityName varchar2(10) PATH ‘/Invoice/MailAddressTo/City‘, State varchar2(5) PATH ‘/Invoice/MailAddressTo/State‘, Zipcode varchar2(7) PATH ‘/Invoice/MailAddressTo/Zipcode‘ ) a WHERE a.CityName like ‘New%‘; INV_ID PERSONNAME STREETNAME CITYNAME STATE ZIPCODE 1 Joe Smith 10 Apple Tree Lane New York NY 12345 執(zhí)行 XML如文中所述,我們已經(jīng)可以執(zhí)行查看 XML 文檔的查詢。下一步是考慮使這些查詢快速執(zhí)行的方式??梢越忉?XML 查詢并創(chuàng)建索引來幫助提高 XML 數(shù)據(jù)訪問的性能。特別地,我們能夠以類似過去調(diào)整 SQL 的方式改善 XPath 函數(shù)的性能。此外,對于 SQL 調(diào)整,在某些情況中,重構(gòu) XML 查詢也有助于更改和改進(jìn)訪問路徑。 基于函數(shù)的索引可用在結(jié)構(gòu)化和非結(jié)構(gòu)化的 XMLType 表上,無論它們是否基于模式。我們還可能想對諸如 existsNode 的函數(shù)利用二進(jìn)制索引,即根據(jù)謂詞評估為 True 或 False 而只返回標(biāo)志 0 或 1。 在本節(jié)中,我們將通過示例來了解一下結(jié)構(gòu)化和非結(jié)構(gòu)化索引通過解釋計(jì)劃會(huì)對性能產(chǎn)生怎樣的潛在影響。首先,優(yōu)化 XQuery 表達(dá)式,該表達(dá)式針對關(guān)系表使用 ora:view。 用 XQuery 表達(dá)式優(yōu)化關(guān)系數(shù)據(jù)。以下是一個(gè)調(diào)整示例,其中使用的是已分析過的類似查詢。該查詢使用 ora:view 訪問關(guān)系數(shù)據(jù)并利用 FLWOR 表達(dá)式。連接 HR 模式的關(guān)系表 Employees 和 Departments,并針對 commission_pct 大于 3 的所有員工返回部門信息。請注意,屬性名(例如,$emp/ROW/COMMISSION_PCT 中的 JOB)區(qū)分大小寫。 在下面的查詢中,F(xiàn)OR 允許我們在 Departments 中迭代行元素 Employees。Employees 行綁定到變量 $emp,而 Departments 行綁定到 $dep。WHERE 執(zhí)行這兩個(gè)表的連接,并選擇 commission_pct 大于 3 的所有員工。RETURN 返回部門信息。 該查詢和解釋計(jì)劃如下所示: explain plan for SELECT XMLQuery(‘for $emp in ora:view("EMPLOYEES"), $dep in ora:view("DEPARTMENTS") where $emp/ROW/DEPARTMENT_ID = $dep/ROW/DEPARTMENT_ID and $emp/ROW/COMMISSION_PCT > .3 return $dep‘ RETURNING CONTENT) AS high_commission_employees FROM DUAL; QUERY_PLAN OBJECT_NAME COST BYTES LEVEL SELECT STATEMENT 2 1 SORT 82 2 HASH JOIN 5 328 3 TABLE ACCESS EMPLOYEES 2 104 4 TABLE ACCESS DEPARTMENTS 2 1512 4 FAST DUAL 2 2 如您所見,優(yōu)化程序選擇的訪問路徑不使用索引。由于 ora:view 允許我們將關(guān)系查詢的結(jié)果返回為 XML 元素,因此我們完全可以在關(guān)系表上創(chuàng)建 b 樹索引并查看這是否可以提高性能。 Create index emp_idx1 on employees (department_id, commission_pct); Create index dept_idx1 on departments (department_id); QUERY_PLAN OBJECT_NAME COST BYTES LEVEL SELECT STATEMENT 2 1 SORT 82 2 TABLE ACCESS DEPARTMENTS 1 56 3 NESTED LOOPS 2 328 4 INDEX EMP_IDX1 1 104 5 INDEX DEPT_IDX1 0 5 FAST DUAL 2 2 現(xiàn)在,這些索引獲得了使用,成本略有降低。我們使用 ora:view 對 XQuery 的調(diào)整非常類似于對標(biāo)準(zhǔn)的非 XML SQL 的調(diào)整。對于標(biāo)準(zhǔn) SQL,重寫該查詢并使用諸如綁定變量(而非硬編碼的字符串值)的特性將有助于提高性能。 優(yōu)化結(jié)構(gòu)化 XMLType 數(shù)據(jù)。XPath 重寫可以優(yōu)化結(jié)構(gòu)化(對象關(guān)系)存儲(chǔ)技術(shù)。該優(yōu)化程序可以進(jìn)行另一個(gè)內(nèi)部性能改進(jìn):當(dāng)以下內(nèi)容為真時(shí),它可以將基于 XPath 的函數(shù)更改為關(guān)系語句:
這提供了由于性能原因進(jìn)行查詢重寫的可能性。它也允許使用關(guān)系性能調(diào)整技術(shù),如上所示。 現(xiàn)在,我們優(yōu)化針對結(jié)構(gòu)化、非關(guān)系 XMLType 數(shù)據(jù)的查詢。首先看一個(gè)非常簡單的示例,它使用早前創(chuàng)建的 invoicexml_tbl 表。 explain plan for select extract(object_value, ‘/Invoice/MailAddressTo‘) from invoicexml_tbl where extractValue(object_value, ‘/Invoice/MailAddressTo/Person‘)= ‘Joe Smith‘; The access path is shown below. QUERY_PLAN OBJECT_NAME COST BYTES LEVEL SELECT STATEMENT 3 87 1 TABLE ACCESS INVOICEXML_TBL 3 87 2 可以創(chuàng)建該查詢中使用的針對謂詞的基于函數(shù)的索引。該索引必須使用與將使用它的查詢完全相同的語法。該索引將創(chuàng)建并多次解釋 SQL 以查看是否使用了新索引。 Create index invoicexml_tbl_idx1 on invoicexml_tbl (extractValue(object_value, ‘/Invoice/MailAddressTo/Person‘)); QUERY_PLAN OBJECT_NAME COST BYTES LEVEL SELECT STATEMENT 1 87 1 TABLE ACCESS INVOICEXML_TBL 1 87 2 INDEX INVOICEXML_TBL_IDX1 1 3 使用了新的基于函數(shù)的索引后,查詢成本將減少。 優(yōu)化非結(jié)構(gòu)化 XMLType 數(shù)據(jù)。 create table invtest_unstruct of XMLtype XMLType store as CLOB; Insert into invtest_unstruct values ( XMLType(bfilename(‘XMLDIR‘, ‘invoicexml.txt‘), nls_charset_id(‘WE8MSWIN1252‘) )); explain plan for select extract(object_value, ‘/Invoice/MailAddressTo‘) from invtest_unstruct where extractValue(object_value, ‘/Invoice/MailAddressTo/Person‘)=‘Joe Smith‘ / QUERY_PLAN OBJECT_NAME COST BYTES LEVEL SELECT STATEMENT 2 2002 1 TABLE ACCESS INVTEST_UNSTRUCT 2 2002 2 現(xiàn)在,我們將創(chuàng)建一個(gè)基于函數(shù)的索引,來查看是否將使用它以及是否減少了預(yù)期的成本。請注意,“create index”語法與查詢謂詞中使用的語法相同。 Create index invtest_unstruct_idx1 on invtest_unstruct (extractValue(object_value, ‘/Invoice/MailAddressTo/Person‘)); QUERY_PLAN OBJECT_NAME COST BYTES LEVEL SELECT STATEMENT 1 2002 1 TABLE ACCESS INVTEST_UNSTRUCT 1 2002 2 INDEX INVTEST_UNSTRUCT_IDX1 1 3 正如您看到的,相同的索引和查詢可以有效用于結(jié)構(gòu)化和非結(jié)構(gòu)化數(shù)據(jù)。但需要注意到,結(jié)構(gòu)化數(shù)據(jù)訪問通常比非結(jié)構(gòu)化訪問更高效。 結(jié)論Oracle 數(shù)據(jù)庫已經(jīng)快速發(fā)展為將 XML 標(biāo)準(zhǔn)與數(shù)據(jù)合并在內(nèi)。Oracle9i 數(shù)據(jù)庫引入了 XML DB 信息庫和一個(gè)新數(shù)據(jù)類型 XMLType,后者提供 LOB 以及結(jié)構(gòu)化存儲(chǔ)選項(xiàng)。利用 Oracle XML DB 特性,通過將 URI 映射為數(shù)據(jù)庫對象的層次模型,您能夠在數(shù)據(jù)庫中執(zhí)行本機(jī) XML 處理。通過使用節(jié)點(diǎn)和路徑概念的“邏輯樹”,可使用 XPath 表達(dá)式操作文檔的單個(gè)元素。Oracle9i 數(shù)據(jù)庫中還包括 XMLSchema 支持。 Oracle 數(shù)據(jù)庫 10g 通過 W3C XML XQuery 語言增強(qiáng)了 XML 支持,該語言包括 XMLQuery() 和 XMLTable() 函數(shù)。這些特性的組合簡化了 Oracle 數(shù)據(jù)庫 10g 第 2 版中關(guān)系數(shù)據(jù)和 XML 數(shù)據(jù)間的相互轉(zhuǎn)換使用。正如本文所述,SQL 查詢可以操作 XML 數(shù)據(jù),而 XML 查詢現(xiàn)在能夠訪問關(guān)系數(shù)據(jù)。其中的關(guān)鍵是 Oracle 對 XMLQuery() 和 XMLTable() 的 SQL/XML 實(shí)現(xiàn)。 Oracle 的基于標(biāo)準(zhǔn)的方法也支持流行的協(xié)議(例如,F(xiàn)TP、HTTP 和 WebDAV),以便允許常用客戶端工具和應(yīng)用程序訪問、編輯和發(fā)布存儲(chǔ)在數(shù)據(jù)庫中的 XML 數(shù)據(jù)。 通過本文,您可輕松增強(qiáng)自己在處理關(guān)系數(shù)據(jù)時(shí)獲取的技巧,進(jìn)而使用這些 SQL/XML 特性來處理 XML。其中重要的一點(diǎn)是,我們能夠通過 Oracle 的索引、解釋和存儲(chǔ)特性來提高 XML 數(shù)據(jù)訪問的性能。對于設(shè)計(jì)人員、開發(fā)人員和 DBA 來說,及時(shí)了解這個(gè)快速變化的技術(shù)是很關(guān)鍵的。 Tim Quinlan [tquinlan@tlqconsulting.com] 是一名 Oracle 認(rèn)證的數(shù)據(jù)庫管理員。他從 1981 年就開始從事數(shù)據(jù)庫工作,從 Oracle 7 到 Oracle10g 第 2 版均有接觸。擔(dān)任過企業(yè)級數(shù)據(jù)倉庫和事務(wù)數(shù)據(jù)庫的 DBA、架構(gòu)師、設(shè)計(jì)者和實(shí)現(xiàn)者。Tim 在許多會(huì)議上發(fā)表過演講,教授過數(shù)據(jù)庫課程,并為很多暢銷的數(shù)據(jù)庫出版物撰寫過專題文章。他的主要(專業(yè))興趣是設(shè)計(jì)和實(shí)現(xiàn)高性能、高可用性的大型數(shù)據(jù)庫系統(tǒng)。 |
|