相同或不同的表
XML 數據類型列可以在包含其他關系列的表中創建,也可以在與主表之間具有外鍵關系的獨立表中創建。
在滿足下列某個條件時,請在同一個表中創建 XML 數據類型列:
• 您的應用程序在 XML 列上執行數據檢索,并且不需要 XML 列上的 XML 索引。 或者
• 您需要在 XML 數據類型列上生成 XML 索引,并且主表的主鍵與其聚集鍵相同。有關詳細信息,請參閱將 XML 數據類型列編入索引一節。
在滿足下列條件時,請在單獨的表中創建 XML 數據類型列:
• 您需要在 XML 數據類型列上生成 XML 索引,但主表的主鍵與其聚集鍵不同,或者主表不具有主鍵,或者主表是一個堆(也就是說,沒有聚集鍵)。如果主表已經存在,則這可能是真的。
• 您不希望表掃描由于表中存在 XML 列(無論它是存儲在行中還是存儲在行外,都會占用空間)而降低速度。
XML 數據的粒度
XML 列中存儲的 XML 數據的粒度對于鎖定和更新特性而言至關重要。SQL Server 對 XML 和非 XML 數據采用了相同的鎖定機制。因此,行級鎖定會導致相應行中的所有 XML 實例被鎖定。當粒度比較大時,鎖定大型 XML 實例以便進行更新會導致多用戶場合下的吞吐量下降。另一方面,嚴重的分解會丟失對象封裝并提高重新組合成本。
對 XML 實例進行更新會將現有實例替換為更新后的實例,即使只修改單個屬性的值。XML 數據粒度越大,更新成本就越高。較小的 XML 實例會產生較高的更新性能。
對于良好的設計而言,重要的是保持數據建模需要與鎖定和更新特性之間的平衡。
非類型化、類型化和受約束的 XML 數據類型
SQL Server 2005 XML 數據類型實現了 ISO SQL-2003 標準 XML 數據類型。因此,它可以在非類型化 XML 列中存儲格式規范的 XML 1.0 文檔以及帶有文本節點和任意數量頂級元素的所謂的 XML 內容片段。系統將檢查數據的格式是否規范,不要求將列綁定到 XML 架構,并且拒絕在擴展意義上格式不規范的數據。對于非類型化 XML 變量和參數,也是如此。
如果您具有描述 XML 數據的 XML 架構,則可以將這些架構與 XML 列相關聯,以便產生類型化 XML。XML 架構用于對數據進行有效性驗證,在查詢和數據修改語句編譯過程中執行比非類型化 XML 更準確的類型檢查,以及優化存儲和查詢處理。
在下列條件下,請使用非類型化 XML 數據類型:
• 您沒有對應于 XML 數據的架構
• 您擁有架構,但不希望服務器對數據進行有效性驗證。當應用程序在服務器中存儲數據之前執行客戶端驗證時,或者暫時存儲根據架構無效的 XML 數據時,或者使用在服務器中不受支持的架構組件(例如 key/keyref)時,有時會出現這種情況。
在下列條件下,請使用類型化 XML 數據類型:
• 您擁有對應于 XML 數據的架構,并且希望服務器按照 XML 架構對 XML 數據進行有效性驗證。
• 您希望充分利用基于類型信息的存儲和查詢優化。
• 您希望在查詢的編譯過程中更充分地利用類型信息。
類型化 XML 列、參數和變量可以存儲 XML 文檔或內容 - 您在聲明時必須將它們指定為標志(分別指定為 DOCUMENT 或 CONTENT)。而且,您必須提供 XML 架構集合。如果每個 XML 實例恰好有一個頂級元素,請指定 DOCUMENT;否則,請使用 CONTENT。查詢編譯器在查詢編譯過程的類型檢查中使用 DOCUMENT 標記來推理唯一的頂級元素。
除了將 XML 列類型化以外,您還可以在類型化或非類型化 XML 數據類型列上使用關系(列或行)約束。在下列條件下,請使用約束:
• 無法在 XML 架構中表示業務規則。例如,花店的送貨地址必須在其營業地點周圍 50 英里范圍之內,這可以編寫為 XML 列上的約束。該約束可能涉及到 XML 數據類型方法。
• 您的約束涉及到表中的其他 XML 列或非 XML 列。這方面的一個例子是:強制 XML 實例中存在的 Customer ID (/Customer/@CustId) 與關系 CustomerID 列中的值匹配。
文檔類型定義 (DTD)
XML 數據類型列、變量和參數可以使用 XML 架構而不是 DTD 加以類型化。然而,對于非類型化和類型化 XML,都可以使用內聯 DTD 來提供默認值,以便將實體引用替換為它們的擴展形式。
您可以使用第三方工具將 DTD 轉化為 XML 架構文檔,并且將 XML 架構加載到數據庫中。
將 XML 數據類型列編入索引
可以在 XML 數據類型列上創建 XML 索引。這會將該列中 XML 實例上的所有標記、值和路徑編入索引,從而提高查詢性能。在下列條件下,您的應用程序可能受益于 XML 索引:
• 對 XML 列進行查詢在您的工作負荷中很常見。必須考慮數據修改過程中的 XML 索引維護成本。
• XML 值相對較大,而檢索的部分相對較小。生成索引可以避免在運行時分析全部數據,并且因為受益于索引查找而提高查詢處理的性能。
XML 列上的第一個索引是"主 XML 索引"。通過該索引,可以在 XML 列上創建三種類型的輔助 XML 索引,從而提高常見種類的查詢的速度,如下節所述。
主 XML 索引
這會將 XML 列中的 XML 實例內部的所有標記、值和路徑編入索引。基表(即包含 XML 列的表)必須在該表的主鍵上具有聚集索引;主鍵用于將索引行與基表中的行相關聯。從 XML 列中檢索完整的 XML 實例(例如 SELECT *)。查詢使用主 XML 索引,并返回標量值或使用索引本身的 XML 子樹。
示例:創建主 XML 索引
在我們的多數示例中,都使用帶有非類型化 XML 列的表 T (pk INT PRIMARY KEY, xCol XML),這些示例都可以簡單地擴展為類型化 XML 的形式(有關使用類型化 XML 的信息,請參閱 SQL Server 2005 聯機圖書)。為了便于說明,將針對如下所示的 XML 數據實例描述查詢:
|
下面的語句在表 T 的 XML 列 xCol 上創建了名為 idx_xCol 的 XML 索引:
|
輔助 XML 索引
在創建主 XML 索引之后,您可能希望創建輔助 XML 索引來提高工作負荷中的不同種類查詢的速度。三種類型的輔助 XML 索引 - PATH、PROPERTY 和 VALUE 分別為基于路徑的查詢、自定義屬性管理場合和基于值的查詢提供幫助。PATH 索引在列中的所有 XML 實例上,按照文檔順序生成各個 XML 節點的 (path, value) 對的 B+ 樹。PROPERTY 索引創建各個 XML 實例中 (PK, path, value) 對的聚集 B+ 樹,其中 PK 是基表的主鍵。最后,VALUE 索引在 XML 列中的所有 XML 實例中,按照文檔順序創建各個節點的 (value, path) 對的 B+ 樹。
以下是創建上述一個或多個索引的一些準則:
• 如果工作負荷大量使用 XML 列中的路徑表達式,則 PATH 輔助 XML 索引可能會加快工作負荷的處理速度。最常見的例子是在 T-SQL 的 WHERE 子句中對 XML 列使用 exist() 方法。
• 如果您的工作負荷從單獨的使用路徑表達式的 XML 實例中檢索多個值,則將各個 XML 實例中的路徑聚集到 PROPERTY 索引中可能會很有用。這種情況通常出現在屬性包場合中,此時對象的屬性被獲取并且其主鍵值已知。
• 如果您的工作負荷涉及到查詢 XML 實例中的值,而不知道包含這些值的元素或屬性名稱,則您可能需要創建 VALUE 索引。這通常發生在子代軸查找中,例如 //author[last-name="Howard"],其中 元素可以出現在層次結構的任意級別上。這種情況還會發生在"通配符"查詢中,例如
/book [@* = "novel"],其中查詢將查找具有某個值為 "novel" 的屬性的 元素。
示例:基于路徑的查找
假設下面的查詢在您的工作負荷中很常見:
|
路徑表達式 /book/@genre 和值 "novel" 對應于 PATH 索引的鍵字段。因此,PATH 類型的輔助 XML 索引對于該工作負荷很有用:
|
示例:獲取對象的屬性
請考慮下面的查詢,它從表 T 的各個行中檢索一本書的屬性"genre"、"title"和 ISBN:
|
在這種情況下,屬性索引很有用,其創建方式如下所示:
|
示例:基于值的查詢
在以下查詢中,子代軸或自身軸 (//) 指定了部分路徑,以便基于 ISBN 值的查找可以因為使用 VALUE 索引而受益:
|
VALUE 索引按如下方式創建:
|
XML 列上的全文索引
您可以在 XML 列上創建全文索引,從而將 XML 值的內容編入索引,而忽略 XML 標記。屬性值沒有被編入全文索引(因為它們被視為標記的一部分),并且元素標記被用作標記邊界。在某些情況下,可以將全文搜索與 XML 索引用法結合起來:
• 首先,使用 SQL 全文搜索篩選感興趣的 XML 值。
• 接下來,查詢這些 XML 值,這會使用 XML 列上的 XML 索引。
示例:將全文搜索與 XML 查詢結合起來
在 XML 列上創建全文索引之后,以下查詢將檢查 XML 值是否在書名中包含單詞"custom":
|
CONTAINS() 方法使用全文索引,將文檔中任何地方包含單詞"custom"的 XML 值組合為一個子集。exist() 子句確保單詞"custom"出現在書名中。
使用 CONTAINS() 和 XQuery contains() 的全文搜索具有不同的語義。后者是子字符串匹配,而前者則是使用單詞衍生的標記匹配。因此,如果要搜索標題中的字符串 "run",則 "run"、"runs" 和 "running" 都將匹配,因為全文 CONTAINS() 和 Xquery contains() 都滿足。然而,上述查詢不匹配標題中的單詞"customizable"。(全文 CONTAINS() 失敗,而 Xquery contains() 被滿足)。通常,對于純粹的子字符串匹配,應該刪除全文 CONTAINS() 子句。
而且,全文搜索采用單詞衍生,而 XQuery contains() 是一種字面匹配。這一區別將在下一個示例中闡述。
示例:使用單詞衍生對 XML 值進行全文搜索
通常情況下,不能排除示例:將全文搜索與 XML 查詢結合起來中的 XQuery contains() 檢查。請考慮查詢:
|
因為使用單詞衍生,所以文檔中的單詞"ran"匹配搜索條件。而且,使用 XQuery 時不會檢查搜索上下文。
在使用被全文索引的 AXSD 將 XML 分解到關系列中時,XML 視圖上的 XPath 查詢不會對基礎表執行全文搜索。
屬性提升
如果主要是對少量元素和屬性值進行查詢(例如,基于客戶 ID 查找客戶,即指定了 /Customer/@CustId 的值),您可能希望將這些數量提升到關系列中。當檢索了整個 XML 實例,但只對一小部分 XML 數據進行查詢時,這將很有用。在 XML 列上創建 XML 索引是沒有必要的;相反,可以將被提升的列編入索引。必須編寫查詢以使用提升的列(即,查詢優化器不會將對 XML 列的查詢重新定向到提升的列)。
提升的列可以是同一表中的計算列,也可以是表中單獨的、用戶維護的列。當從各個 XML 實例中提升唯一值(即單值屬性)時,這已足夠。然而,對于多值屬性,您必須為該屬性創建單獨的表,如下所述。
基于 XML 數據類型的計算列
可以使用能夠激活 XML 數據類型方法的用戶定義函數 (UDF) 來創建計算列。計算列的類型可以是任何 SQL 類型,包括 XML。以下示例說明了這一點。
示例:基于 XML 數據類型方法的計算列
為書籍的 ISBN 創建用戶定義的函數:
|
為 ISBN 向表中添加一個計算列:
|
可以用通常的方式將計算列編入索引。
示例:基于 XML 數據類型方法的計算列上的查詢
要獲取其 ISBN 為 0-7356-1588-2 的 ,可以改寫 XML 列上的查詢
|
以使用計算列,如下所示:
|
可以創建一個用戶定義的函數,返回 XML 數據類型和使用該 UDF 的計算列。然而,無法在計算的 XML 列上創建 XML 索引。
創建屬性表
您可能希望將 XML 數據中的某些多值屬性提升到一個或多個表中,在這些表上創建索引,并且重定向查詢以使用這些表。典型的情形是一小部分屬性覆蓋了大部分查詢工作負荷。您可以執行以下操作:
• 創建一個或多個表以存放多值屬性。您可能發現采用以下處理方式會很方便:每個表存儲一個屬性,并且在屬性表中復制基表的主鍵以便與基表進行向后聯接。
• 如果您希望保持屬性的相對順序,則需要為相對順序引入一個單獨的列。
• 在 XML 列上創建觸發器以便維護屬性表。在觸發器中,執行以下操作:
• 使用 XML 數據類型方法(如 nodes() 和 value())在屬性表中插入和刪除行。(有關 nodes() 方法的詳細信息,請參閱 Value()、Nodes() 和 OpenXML()。)
• 在 CLR 中創建流式表值函數,以便在屬性表中插入和刪除行。
• 編寫查詢,以便對屬性表進行 SQL 訪問,以及對基表中的 XML 列進行 XML 訪問,這需要使用這些表的主鍵將其相互聯接。
示例:創建屬性表
假設您希望提升作者的名字。書籍有一個或多個作者,因此名字是一個多值屬性。每個名字都存儲在屬性表的單獨行中。在屬性表中復制了基表的主鍵以便向后聯接。
|
示例:創建用戶定義的函數以便從 XML 實例生成行集
下面的表值函數 udf_XML2Table 接受一個主鍵值和一個 XML 實例。它將檢索 元素的所有作者的名字,并返回(主鍵,名字)對行集。
|
示例:創建觸發器以填充屬性表
插入觸發器:在屬性表中插入行
|
刪除觸發器:基于刪除行的主鍵值,從屬性表中刪除行
|
更新觸發器:在與更新的 XML 實例對應的屬性表中刪除現有行,并且在該屬性表中插入新行
|
示例:查找作者的名字為"David"的 XML 實例
可以在 XML 列上表示該查詢。另外,還可以在屬性表中搜索名字"David",然后與基表執行向后聯接以返回 XML 實例,如下所示:
|
示例:使用 CLR 流式表值函數的解決方案
該解決方案包括以下步驟:(a) 定義 CLR 類 SqlReaderBase,它實現了 ISqlReader,并且通過在 XML 實例上應用路徑表達式來生成流式表值輸出;(b) 創建一個程序集和一個 T-SQL 用戶定義函數 (UDF) 來激活該 CLR 類;(c) 使用 UDF 定義插入、更新和刪除觸發器,以維護屬性表。
首先,創建流式 CLR 函數,其主干如下所示。XML 數據類型被公開為 ADO.NET 中的托管類 SqlXml;它支持返回 XmlReader 的方法 CreateReader()。
|
接下來,創建一個程序集,以及一個與 CLR 函數 streaming_xml_tvf 對應的 T-SQL 用戶定義函數 SQL_streaming_xml_tvf(未顯示)。該 UDF 用于定義表值函數 CLR_udf_XML2Table 以便生成行集:
|
最后,定義觸發器,如示例創建觸發器以填充屬性表中所示,但用函數 CLR_udf_XML2Table 替換 udf_XML2Table。因此,插入觸發器將如下所示:
|
刪除觸發器與非 CLR 版本完全相同,而更新觸發器只是將函數 udf_XML2Table() 替換為 CLR_udf_XML2Table()。
XML 架構集合
XML 架構集合是一個元數據實體,其范圍由關系架構確定,包含一個或多個可能相關(例如,通過 )或無關的 XML 架構。XML 架構集合中的單獨 XML 架構由其目標命名空間標識。
XML 架構集合是使用 CREATE XML SCHEMA COLLECTION 語法創建的,并且提供了一個或多個 XML 架構。可以向現有 XML 架構中添加更多的 XML 架構組件,并且可以使用 ALTER XML SCHEMA COLLECTION 語法向 XML 架構集合中添加更多的架構。可以使用 SQL Server 2005 中的安全模型像任何 SQL 對象那樣保證 XML 架構集合的安全。
多類型化列
XML 架構集合 C 按照多個 XML 架構將 XML 列 xCol 類型化。此外,標志 DOCUMENT 或 CONTENT 分別指定 XML 樹或片段是否可以存儲在列 xCol 中。
對于 DOCUMENT,每個 XML 實例都會按照用來對其進行驗證和類型化的命名空間,指定實例中頂級元素的目標命名空間。另一方面,對于 CONTENT,每個頂級元素都可以指定 C 中的任一目標命名空間。XML 實例將按照實例中存在的所有目標命名空間進行驗證和類型化。
架構演變
XML 架構集合用于類型化 XML 列、變量和參數。它提供了一種 XML 架構演變機制。假設您將帶有目標命名空間 BOOK-V1 的 XML 架構添加到 XML 架構集合 C 中。使用 C 加以類型化的 XML 列 xCol 可以存儲符合 BOOK-V1 架構的 XML 數據。
假設某個應用程序希望通過新的架構組件(例如復雜類型定義和頂級元素聲明)來擴展 XML 架構。這些新的架構組件可以添加到 BOOK-V1 架構中,并且不要求對列 xCol 中的現有 XML 數據進行重新驗證。
假設該應用程序后來希望提供該 XML 架構的新版本,并且為該新版本選擇目標命名空間 BOOK-V2。該 XML 架構可以添加到 C 中。XML 列可以存儲 BOOK-V1 和 BOOK-V2 二者的實例,并且對符合這些命名空間的 XML 實例執行查詢和數據修改。
(T114)