T-Sql操作Xml數據
一、前言
SQL Server 2005 引入了一種稱爲 XML 的本機數據類型。用戶可以創建這樣的表,它在關係列之外還有一個或多個 XML 類型的列;此外,還允許帶有變量和參數。爲了更好地支持 XML 模型特徵(例如文檔順序和遞歸結構),XML 值以內部格式存儲爲大型二進制對象 (BLOB)。
用戶將一個XML數據存入數據庫的時候,可以使用這個XML的字符串,SQL Server會自動的將這個字符串轉化爲XML類型,並存儲到數據庫中。
隨着SQL Server 對XML字段的支持,相應的,T-SQL語句也提供了大量對XML操作的功能來配合SQL Server中XML字段的使用。本文主要說明如何使用SQL語句對XML進行操作。
二、定義XML字段
在進行數據庫的設計中,我們可以在表設計器中,很方便的將一個字段定義爲XML類型。需要注意的是,XML字段不能用來作爲主鍵或者索引鍵。同樣,我們也可以使用SQL語句來創建使用XML字段的數據表,下面的語句創建一個名爲“docs”的表,該錶帶有整型主鍵“pk”和非類型化的 XML 列“xCol”:
CREATE TABLE docs (pk INT PRIMARY KEY, xCol XML not null)
XML類型除了在表中使用,還可以在存儲過程、事務、函數等中出現。下面我們來完成我們對XML操作的第一步,使用SQL語句定義一個XML類型的數據,併爲它賦值:
declare @xmlDoc xml;
set @xmlDoc='<book id="0001">
<title>C Program</title>
<author>David</author>
<price>21</price>
</book>'
三、查詢操作
在定義了一個XML類型的數據之後,我們最常用的就是查詢操作,下面我們來介紹如何使用SQL語句來進行查詢操作的。
在T-Sql中,提供了兩個對XML類型數據進行查詢的函數,分別是query(xquery)和value(xquery, dataType),其中,query(xquery)得到的是帶有標籤的數據,而value(xquery, dataType)得到的則是標籤的內容。接下類我們分別使用這兩個函數來進行查詢。
1、使用query(xquery) 查詢
我們需要得到書的標題(title),使用query(xquery)來進行查詢,查詢語句爲:
select @xmlDoc.query('/book/title')
運行結果如圖:
2、使用value(xquery, dataType) 查詢
同樣是得到書的標題,使用value函數,需要指明兩個參數,一個爲xquery, 另一個爲得到數據的類型。看下面的查詢語句:
select @xmlDoc.value('(/book/title)[1]', 'nvarchar(max)')
運行結果如圖:
3、查詢屬性值
無論是使用query還是value,都可以很容易的得到一個節點的某個屬性值,例如,我們很希望得到book節點的id,我們這裏使用value方法進行查詢,語句爲:
select @xmlDoc.value('(/book/@id)[1]', 'nvarchar(max)')
運行結果如圖:
4、使用xpath進行查詢
xpath是.net平臺下支持的,統一的Xml查詢語句。使用XPath可以方便的得到想要的節點,而不用使用where語句。例如,我們在@xmlDoc中添加了另外一個節點,重新定義如下:
set @xmlDoc='<root>
<book id="0001">
<title>C# Program</title>
<author>Jerry</author>
<price>50</price>
</book>
<book id="0002">
<title>Java Program</title>
<author>Tom</author>
<price>49</price>
</book>
</root>'
--得到id爲0002的book節點
select @xmlDoc.query('(/root/book[@id="0002"])')
上面的語句可以獨立運行,它得到的是id爲0002的節點。運行結果如下圖:
四、修改操作
SQL的修改操作包括更新和刪除。SQL提供了modify()方法,實現對Xml的修改操作。modify方法的參數爲XML修改語言。XML修改語言類似於SQL 的Insert、Delete、UpDate,但並不一樣。
1、修改節點值
我們希望將id爲0001的書的價錢(price)修改爲100, 我們就可以使用modify方法。代碼如下:
set @xmlDoc.modify('replace value of (/root/book[@id=0001]/price/text())[1] with "100"')
--得到id爲0001的book節點
select @xmlDoc.query('(/root/book[@id="0001"])')
注意:modify方法必須出現在set的後面。運行結果如圖:
2、刪除節點
接下來我們來刪除id爲0002的節點,代碼如下:
--刪除節點id爲0002的book節點
set @xmlDoc.modify('delete /root/book[@id=0002]')
select @xmlDoc
運行結果如圖:
3、添加節點
很多時候,我們還需要向xml裏面添加節點,這個時候我們一樣需要使用modify方法。下面我們就向id爲0001的book節點中添加一個ISBN節點,代碼如下:
--添加節點
set @xmlDoc.modify('insert 78-596-134 before (/root/book[@id=0001]/price)[1]')
select @xmlDoc.query('(/root/book[@id="0001"]/isbn)')
運行結果如圖:
4、添加和刪除屬性
當你學會對節點的操作以後,你會發現,很多時候,我們需要對節點進行操作。這個時候我們依然使用modify方法,例如,向id爲0001的book節點中添加一個date屬性,用來存儲出版時間。代碼如下:
--添加屬性
set @xmlDoc.modify('insert attribute date{"2008-11-27"} into (/root/book[@id=0001])[1]')
select @xmlDoc.query('(/root/book[@id="0001"])')
運行結果如圖:
如果你想同時向一個節點添加多個屬性,你可以使用一個屬性的集合來實現,屬性的集合可以寫成:(attribute date{"2008-11-27"}, attribute year{"2008"}),你還可以添加更多。這裏就不再舉例了。
5、刪除屬性
刪除一個屬性,例如刪除id爲0001 的book節點的id屬性,我們可以使用如下代碼:
--刪除屬性
set @xmlDoc.modify('delete root/book[@id="0001"]/@id')
select @xmlDoc.query('(/root/book)[1]')
運行結果如圖:
6、修改屬性
修改屬性值也是很常用的,例如把id爲0001的book節點的id屬性修改爲0005,我們可以使用如下代碼:
--修改屬性
set @xmlDoc.modify('replace value of (root/book[@id="0001"]/@id)[1] with "0005"')
select @xmlDoc.query('(/root/book)[1]')
運行結果如圖:
exist()方法,用來判斷指定的節點是否存在,返回值爲true或false; nodes()方法,用來把一組由一個查詢返回的節點轉換成一個類似於結果集的表中的一組記錄行。
declare @data xml
set @data='
<bookstore>
<book category="COOKING">
<title lang="en">Everyday Italian</title>
<author>Giada De Laurentiis</author>
<year>2005</year>
<price>30.00</price>
</book>
<book category="CHILDREN">
<title lang="jp">Harry Potter</title>
<author>J K. Rowling</author>
<year>2005</year>
<price>29.99</price>
</book>
<book category="WEB">
<title lang="en">XQuery Kick Start</title>
<author>James McGovern</author>
<author>Per Bothner</author>
<author>Kurt Cagle</author>
<author>James Linn</author>
<author>Vaidyanathan Nagarajan</author>
<year>2003</year>
<price>49.99</price>
</book>
<book category="WEB">
<title lang="cn">Learning XML</title>
<author>Erik T. Ray</author>
<year>2003</year>
<price>39.95</price>
</book>
</bookstore>
'
--測試語句,如果不理解語法請參考上面的xpath規則和xquery函數說明
--1、文檔
select @data
--2、任意級別是否存在price節點
select @data.exist('//price')
--3、獲取所有book節點
select @data.query('//book')
--4、獲取所有包含lang屬性的節點
select @data.query('//*[@lang]')
--5、獲取第一個book節點
select @data.query('//book[1]')
--6、獲取前兩個book節點
select @data.query('//book[position()<=2]')
--7、獲取最後一個book節點
select @data.query('//book[last()]')
--8、獲取price>35的所有book節點
select @data.query('//book[price>35]')
--9、獲取category="WEB"的所有book節點
select @data.query('//book[@category="WEB"]')
--10、獲取title的lang="en"的所有book節點
select @data.query('//book/title[@lang="en"]')
--11、獲取title的lang="en"且 price>35的所有book節點
select @data.query('//book[./title[@lang="en"] or price>35 ]')
--12、獲取title的lang="en"且 price>35的第一book的(第一個)title
select @data.query('//book[./title[@lang="en"] and price>35 ]').value('(book/title)[1]','varchar(max)')
--13、等價於12
select @data.value('(//book[./title[@lang="en"] and price>35 ]/title)[1]','varchar(max)')
--14、獲取title的lang="en"且 price>35的第一book的(第一個)title的lang屬性
select @data.value('((//book[@category="WEB" and price>35 ]/title)[1]/@lang)[1]','varchar(max)')
--15、獲取第一本書的title
select Tab.Col.value('(book/title)[1]','varchar(max)') as title
from @data.nodes('bookstore')as Tab(Col)
--16、獲取每本書的第一個author
select Tab.Col.value('author[1]','varchar(max)') as title
from @data.nodes('//book')as Tab(Col)
--17、獲取所有book的所有信息
select
T.C.value('title[1]','varchar(max)') as title,
T.C.value('year[1]','int') as year,
T.C.value('title[1]','varchar(max)')as title,
T.C.value('price[1]','float') as price,
T.C.value('author[1]','varchar(max)') as author1,
T.C.value('author[2]','varchar(max)') as author2,
T.C.value('author[3]','varchar(max)') as author3,
T.C.value('author[4]','varchar(max)') as author4
from @data.nodes('//book') as T(C)
--18、獲取不是日語(lang!="jp")且價格大於35的書的所有信息
select
T.C.value('title[1]','varchar(max)') as title,
T.C.value('year[1]','int') as year,
T.C.value('title[1]','varchar(max)')as title,
T.C.value('price[1]','float') as price,
T.C.value('author[1]','varchar(max)') as author1,
T.C.value('author[2]','varchar(max)') as author2,
T.C.value('author[3]','varchar(max)') as author3,
T.C.value('author[4]','varchar(max)') as author4
from @data.nodes('//book[./title[@lang!="jp"] and price>35 ]') as T(C)