SQL Server 2000數據庫FOR XML查詢概述
由于XML本身的諸多優點,XML技術已被廣泛的使用,目前的好多軟件技術同XML緊密相關,比如微軟的.net 平臺對xml提供了強大的支持,提供System.Xml以及其子命名空間下的類型來操作xml。Ado.net通過核心類型DataSet出色的把關系型數據庫同xml進行了緊密集成。由于平常許多開發人員使用.net 來操作SQL Server的到數據集后再轉換成xml,所以往往忽略Transact-SQL查詢生成XML數據的強大功能。對于一些項目使用XML查詢直接通過SQL生成xml會來的更為簡便,所以我通過在實際項目中的使用和查閱一些資料寫成一個知識點,一是溫故而知新,二是對于一些開發者剛好需要這方面的技術而還沒有找到比較快捷的學習方式提供一條途徑。
在SQL SERVER 2000中查詢生成XML的語法表達式比較簡潔,整個語法如下:
SELECT
FROM
WHERE
FOR XML AUTO | RAW | EXPLICIT [,XMLDATA ] [,ELEMENTS] [,BINARY BASE64]
下面我將以Northwind數據庫來演示上面的表達式中所包含的各項功能,下面的查詢語句和返回結果都通過SQL SERVER 2000查詢分析器來執行和得到。
一.使用AUTO模式
該模式我認為在生成單表xml數據方面是用得最多的,能滿足一般的需要。先來看他的簡單查詢。
1.簡單查詢
查詢語句:
SELECT CategoryID,
CategoryName
FROM Categories
WHERE CategoryID < 3 FOR XML AUTO
返回結果:
也可以使用別名,
查詢語句:
SELECT CategoryID AS ID,
CategoryName,
GetDate() as CurrDate
FROM Categories MyTable
WHERE CategoryID < 3 FOR XML AUTO
返回結果:
2.連接查詢
以兩個表為例,
查詢語句:
SELECT Categories.CategoryID,
Categories.CategoryName,
ProductID,
ProductName
FROM Categories
JOIN Products ON Categories.CategoryID = Products.CategoryID AND ProductID <5
WHERE Categories.CategoryID < 3 FOR XML AUTO
返回結果:
可以看到表連接查詢可以生成分層次的Xml,不過需要注意的是SELECT子句中的父表的列要排在子表的列的前面,否則會出現你不想看到的結果,如:
查詢語句:
SELECT ProductID,Categories.CategoryID,Categories.CategoryName,ProductName
FROM Categories
JOIN Products ON Categories.CategoryID = Products.CategoryID and ProductID <5
WHERE Categories.CategoryID <3 FOR XML AUTO
返回結果:
3.使用ELEMENTS選項
使用該選項可以生成以元素為中心的Xml表示,默認為屬性方式,不過屬性方式節省空間。需要注意的是使用ELEMENTS選項是一種全是或全否的形式,不能得到一部分是以元素表示而另一部分以屬性表示的Xml數據。
查詢語句:
SELECT CategoryID,
CategoryName
FROM Categories
WHERE CategoryID < 3
FOR XML AUTO, ELEMENTS
返回結果:
1
Beverages
2
Condiments
在連接查詢時,
查詢語句:
SELECT Categories.CategoryID,
ProductID,
ProductName
FROM Categories
JOIN Products ON Categories.CategoryID = Products.CategoryID and ProductID <4
WHERE Categories.CategoryID <3 FOR XML AUTO, ELEMENTS
返回結果:
1
1
Chai
2
Chang
2
3
Aniseed Syrup
4.檢索對二進制數據的XPath引用
這是對二進制數據的操作,
查詢語句:
SELECT CategoryID,
Picture
FROM Categories
WHERE CategoryID = 1
FOR XML AUTO
返回結果:
使用ELEMENTS方式,
查詢語句:
SELECT CategoryID,
Picture
FROM Categories
WHERE CategoryID = 1
FOR XML AUTO,ELEMENTS
返回結果:
1
dbobject/Categories[@CategoryID='1']/@Picture
二.使用RAW模式
使用RAW模式不能使用ELEMENTS選項。
1.簡單查詢
查詢語句:
SELECT CategoryID,
CategoryName AS Nanme
FROM Categories
WHERE CategoryID < 3
ORDER BY CategoryID DESC
FOR XML RAW
返回結果:
2.連接查詢
查詢語句:
SELECT Categories.CategoryID,
Categories.CategoryName,
ProductID,
ProductName
FROM Categories
JOIN Products ON Categories.CategoryID = Products.CategoryID and ProductID <4
WHERE Categories.CategoryID <= 2 FOR XML RAW
返回結果:
三.使用EXPLICIT模式
該模式使用起來相對比較復雜,不過它可以很靈活的控制返回的xml數據結構。在該查詢中定義了兩個表示元數據的額外列。Tag列唯一的確定用來在結果中表示每一行的xml標記,Parent列用來控制元素之間的嵌套關系。
1.使用通用表
EXPLICIT模式下有一個通用表的概念,使用數據列的名稱來定義xml文檔中的數據。結構如下:
ElementName!TagNumber!AttributeName!Directive
下面的說明來自聯機叢書,
ElementName
是所得到的元素類屬標識符(例如,如果將 Customers 指定為ElementName,則 是元素標記)。
TagNumber
是元素的標記號。借助于通用表中的兩個元數據列(Tag 和 Parent),TagNumber用于表示 XML 樹中的 XML 元素嵌套。每個TagNumber都準確對應于一個ElementName。
AttributeName
是 XML 特性的名稱(如果沒有指定Directive)或包含的元素名(如果Directive是 xml、cdata 或 element)。如果指定Directive,則AttributeName可以為空。這種情況下,列中包含的值直接由具有指定ElementName的元素所包含。
Directive
是可選命令。如果沒有指定Directive,則必須指定AttributeName。如果沒有指定AttributeName且沒有指定Directive(如 Customer!1),則表示元素命令(如 Customer!1!!element)且包含數據。
Directive有兩種用途。該選項用于分別使用關鍵字 ID、IDREF 和 IDREFS 對 ID、IDREF 和 IDREFS 進行編碼。還用于表示如何使用關鍵字 hide、element、xml、xmltext 和 cdata 將字符串數據映射到 XML。大多數情況下允許在這些組中組合指令,但是不能在組本身中進行組合。
ID
可將元素特性指定為 ID 類型的特性。然后可以使用 IDREF 和 IDREFS 特性引用它們,以啟用文檔內的鏈接。但是,如果沒有請求 XMLDATA,則此關鍵字無效。
IDREF
指定為 IDREF 的特性可用于引用 ID 類型的特性,以啟用文檔內的鏈接。但是,如果沒有請求 XMLDATA,則此關鍵字無效。
IDREFS
指定為 IDREFS 的特性可用于引用 ID 類型的特性,以啟用文檔內的鏈接。但是,如果沒有請求 XMLDATA,則此關鍵字無效。
hide
不顯示特性。這對于按照不出現在結果中的特性對結果進行排序可能很有用。
element
不生成特性。而是生成具有指定名稱的包含元素(如果沒有指定特性名則直接生成包含元素)。包含數據被編碼為實體(例如,字符 < 變成 <)。該關鍵字可以與 ID、IDREF 或 IDREFS 組合。
xml
除了不進行實體編碼外,該命令與元素命令相同(例如,字符 < 仍是 <)。除了 hide 外,該命令不能與任何其它命令一起使用。
xmltext
列內容應包在單個標記內,以便與文檔的其它部分集成。如果指定了AttributeName,則標記名由指定名稱替換;否則通過將內容放在容器的起始處而不進行實體編碼,將特性追加到包含元素的當前特性列表。含有該命令的列必須是文本類型(varchar、nvarchar、char、nchar、text、ntext)。該命令只能與 hide 一起使用。在提取存儲在列中的溢出數據時該命令很有用。 如果內容不是有效的 XML,則該行為不明確。
cdata
通過用 CDATA 節環繞數據來包含數據。不對內容進行實體編碼。原始數據類型必須是文本類型(varchar、nvarchar、text、ntext)。該命令只能與 hide 一起使用。當使用該命令時,不應指定AttributeName。
2.簡單查詢
在指定數據所在的元素的名稱和標記時必須使用ElementName參數和Tag參數,也就是查詢語句中表中存在的每一個實際的數據列都必須以ElementName!Tag開頭。在沒有指定AttributeName!Directive的情況下以元素值的形式返回。在只查詢一個表的情況下Tag為1,Parent為NULL。
使用EXPLICIT模式來查詢數據我認為使用倒推的方式會比較方便一些,比如我要得到如下結構,
Description
查詢語句:
SELECT 1 AS Tag,NULL AS Parent,
CategoryID AS [Category!1!ID],
CategoryName AS [Category!1!Name],
Description AS [Category!1]
FROM Categories
WHERE categoryID > 6 FOR XML EXPLICIT
返回結果:
Dried fruit and bean curd
Seaweed and fish
當然下面的語句同上面的等效,
SELECT 1 Tag,NULL Parent,
CategoryID [Category!1!ID],
CategoryName [Category!1!Name],
Description [Category!1]
FROM Categories
WHERE categoryID > 6 FOR XML EXPLICIT
2.連接查詢
在EXPLICIT模式下的多表連接查詢可能相對比較復雜些,先來看一個簡單的例子,
查詢語句:
SELECT 1 AS Tag,NULL AS Parent,
Categories.CategoryID AS [Category!1!ID],
Categories.CategoryName AS [Category!1!!element],
ProductID AS [Category!1!ProductID!element],
ProductName AS [Category!1!ProductName!element]
FROM Categories
JOIN Products ON Categories.CategoryID = Products.CategoryID AND ProductID <5
WHERE Categories.CategoryID < 3 FOR XML EXPLICIT
返回結果:
Beverages
1
Chai
Beverages
2
Chang
Condiments
3
Aniseed Syrup
Condiments
4
Chef Anton's Cajun Seasoning
從上面的結構可以看出數據的xml結構不是很舒服,存在比較大的冗余。想象中改進后的結構如下:
3
Aniseed Syrup
4
Chef Anton's Cajun Seasoning
1
Chai
2
Chang
從上面看出Category元素的Tag為1,Parent為NULL,而Product元素的Tag為2,Parent為1(即Category的Tag值)。這里有一個問題需要解決,那就是在Tag和Parent中存在兩套值,這里就需要使用UNION ALL運算符來實現這項功能,UNION ALL運算符的一項功能就是消除查詢返回的重復的行。使用UNION ALL時需要注意的是結果集的列數必須相同。
查詢語句:
SELECT 1 AS Tag,NULL AS Parent,
CategoryID AS [Category!1!ID],
CategoryName AS [Category!1!Name],
NULL AS [Product!2!ProductID!element],
NULL AS [Product!2!ProductName!element]
FROM Categories WHERE CategoryID < 3
UNION ALL
SELECT 2 AS Tag,1 AS Parent,
Categories.CategoryID AS [Category!1!ID],
Categories.CategoryName AS [Category!1!Name],--這里可以使用NULL代替
ProductID AS [Product!2!ProductID!element],
ProductName AS [Product!2!ProductName!element]
FROM Categories
JOIN Products ON Categories.CategoryID = Products.CategoryID AND ProductID <5
WHERE Categories.CategoryID < 3
ORDER BY [Category!1!ID] DESC,[Product!2!ProductID!element] ASC
FOR XML EXPLICIT
返回結果:
3
Aniseed Syrup
4
Chef Anton's Cajun Seasoning
1
Chai
2
Chang
不帶FOR XML EXPLICIT語句的結果為:
Tag Parent Category!1! ID Category!1!Name Product!2! Product!2!
ProductID!element ProductName!element
---- ------- ---------- ------------ -------------------------- ----------------------------
1 NULL 2 Condiments NULL NULL
2 1 2 Condiments 3 Aniseed Syrup
2 1 2 Condiments 4 Chef Anton's Cajun Seasoning
1 NULL 1 Beverages NULL NULL
2 1 1 Beverages 1 Chai
2 1 1 Beverages 2 Chang
3.使用EXPLICIT模式查詢中的指令
這些指令是指通用表列的第四部分,通過它對數據進行進一步控制。
1> 最常用的element和xml指令
通過該指令把列的數據顯示為一個子元素而不是屬性。
查詢語句:
SELECT 1 AS Tag,NULL AS Parent,
CategoryID AS [Category!1!ID],
CategoryName [Category!1!Name],
Description [Category!1!!element]
FROM Categories
WHERE categoryID > 6 FOR XML EXPLICIT
返回結果:
Dried fruit and bean curd
Seaweed and fish
查詢語句:
SELECT 1 AS Tag,NULL AS Parent,
CategoryID AS [Category!1!ID],
CategoryName [Category!1!Name],
Description [Category!1!Description!element]
FROM Categories
WHERE categoryID > 6 FOR XML EXPLICIT
返回結果:
Dried fruit and bean curd
Seaweed and fish
查詢語句:
SELECT 1 AS Tag,NULL AS Parent,
CategoryID AS [Category!1!ID],
CategoryName [Category!1],
Description [Category!1!Description!element]
FROM Categories
WHERE categoryID > 6 FOR XML EXPLICIT
返回結果:
Produce
Dried fruit and bean curd
Seafood
Seaweed and fish
xml指令同element指令差不多,只是element指令會將一些xml下面的符號進行編碼,如>被編碼成>5,而xml指令不會。
2> 使用hide指令
該指令是用來處理那些不想返回結果的列,許多人會有疑問覺得沒有必要,只要在列中不增加就行了,條件和排序也可以直接使用列。我們可以想得到使用UNION ALL時就用得上了,看下面的例子。
查詢語句:
SELECT 1 AS Tag,NULL AS Parent,
CategoryID AS [Category!1!ID!hide],
CategoryName AS [Category!1!Name],
NULL AS [Product!2!ProductID!element],
NULL AS [Product!2!ProductName!element]
FROM Categories WHERE CategoryID < 3
UNION ALL
SELECT 2 ,1 ,
Categories.CategoryID,
NULL,
ProductID,
ProductName
FROM Categories
JOIN Products ON Categories.CategoryID = Products.CategoryID AND ProductID <5
WHERE Categories.CategoryID < 3
ORDER BY [Category!1!ID!hide] DESC,[Product!2!ProductID!element] ASC
FOR XML EXPLICIT
返回結果:
3
Aniseed Syrup
4
Chef Anton's Cajun Seasoning
1
Chai
2
Chang
3> 使用xmltext指令
該指令是用來處理包含xml片斷內容的數據列,沒找到現成的表結構和數據進行測試,所以就在Categories表中增加了XmlData列名,類型為nvarchar,默認值設置為“”。
查詢語句:
SELECT 1 AS Tag,NULL AS Parent,
CategoryID AS [Category!1!ID],
XmlData AS [Category!1!CustomData!xmltext]
FROM Categories WHERE CategoryID = 8 FOR XML EXPLICIT
返回結果:
查詢語句:
SELECT 1 AS Tag,NULL AS Parent,
CategoryID AS [Category!1!ID],
XmlData AS [Category!1!!xmltext]
FROM Categories WHERE categoryID = 8 FOR XML EXPLICIT
返回結果:
4> 使用cdata指令
使用該指令可以創建CDATA節防止一些字符數據被xml解析器。
查詢語句:
SELECT 1 AS Tag,NULL AS Parent,
CategoryID AS [Category!1!ID],
XmlData AS [Category!1!!cdata]
FROM Categories WHERE categoryID = 6 FOR XML EXPLICIT
返回結果:
]]>
5> 使用ID、IDREF和IDREFS指令以及XMLDATA選項
由于這里只能返回XDR構架,不支持XSD架構的檢索,我就只給出一個例子,
查詢語句:
SELECT 1 AS Tag,NULL AS Parent,
CategoryID AS [Category!1!ID!ID],
CategoryName AS [Category!1!Name],
NULL AS [Product!2!ProductID!IDREF],
NULL AS [Product!2!ProductName]
FROM Categories WHERE CategoryID < 3
UNION ALL
SELECT 2 ,1 ,
Categories.CategoryID,
NULL,
ProductID,
ProductName
FROM Categories
JOIN Products ON Categories.CategoryID = Products.CategoryID AND ProductID <5
WHERE Categories.CategoryID < 3
ORDER BY [Category!1!ID!ID] ,[Product!2!ProductID!IDREF]
FOR XML EXPLICIT,XMLDATA
返回結果:
四.使用BINARY BASE64 選項
使用該選項可以檢索二進制字段,比如圖像數據以base64的形式返回到xml文檔。
1.使用RAW方式
查詢語句:
SELECT CategoryID,
Picture
FROM Categories
WHERE CategoryID =1
ORDER BY CategoryID DESC
FOR XML RAW,BINARY BASE64
返回結果:
2.使用AUTO方式
查詢語句:
SELECT CategoryID,
Picture
FROM Categories
WHERE CategoryID =2
ORDER BY CategoryID DESC
FOR XML AUTO,BINARY BASE64
返回結果:
查詢語句:
SELECT CategoryID,
Picture
FROM Categories
WHERE CategoryID =1
ORDER BY CategoryID DESC
FOR XML AUTO,ELEMENTS,BINARY BASE64
返回結果:
1
FRwvAAIAAAANAA4AFAAhAP////9CaXRt...
