SQL基礎之XML

1.XML數據類型node

  在SQL Server中xml數據類型能夠用來保存xml文檔,這個文檔便可以是完整的xml文檔和xml片斷,這樣開發者就能夠像使用int數據類型同樣來使用xml數據類型。不過xml數據類型是一種特殊的數據類型,它主要存在如下限制。
sql

(1)除了string數據類型外,沒有其餘數據類型可以轉換爲xml數據類型。xml數量列不可用於group by語句中。數據庫

(2)xml列不得成爲主鍵或外鍵的一部分,xml列不能指定爲惟一的,可應用於xml列的內置標量函數只有isnull和coalesce。函數

(3)xml數據類型最多不能超過2gb,表中xml列不得超過32個,xml列不得加入到規則中。學習

2.XQueryspa

  SQL Server爲了更好的讓開發者使用xml數據類型,因而便出現了xml查詢語言XQuery,它能夠查詢結構化甚至是半結構化的xml數據。XQuery可以提供對xml數據的快速存儲和查詢,而且支持迭代以及對結果集進行排序等功能。下面是5個xml數據類型方法的sql語句。設計

use testDb declare @myxml xml set @myxml='<people> <student id="201301"> <Name>王五</Name> <Age>18</Age> <Address>湖南</Address> </student> <student id="201302"> <Name>李一</Name> <Age>20</Age> <Address>湖北</Address> </student> </people>'
--query(QueryString)函數 select @myxml.query('/people/student/Name') --結果: <Name>王五</Name><Name>李一</Name> select @myxml.query(' for $ss in /people/student where $ss/Age[text()]<22 return element Res { (attribute age{data($ss/Age[text()[1]])}) }') --結果爲: <Res age="18" /><Res age="20" />
/* value(QueryString,SQLType),該方法對xml執行XQuery查詢,返回SQL類型的標量值。 參數QueryString:字符串類型的XQuery表達式,它檢索xml實例中的數據而且最多隻返回一個值,不然將返回錯誤 參數SQLType:要返回的首選SQL類型,SQLType不能是XML數據類型、公共語言運行時用戶定義類型、image、text、ntext或sql_variant數據類型。但SQLType能夠是用戶定義數據類型 */ declare @result int set @result=@myxml.value('(/people/student/@id)[1]','int') select @result /* exist(QueryString)方法, 它返回如下三種狀況 1, 表示根據QueryString查詢的結果集爲非空,也就是說至少返回一個節點 0, 表示返回空結果集 null, 表示xml數據類型實例爲null */ declare @ex xml --若是不對@ex賦值,此時@ex爲null,那麼返回的結果將爲null --select @ex.exist('string') --返回NULL set @ex=@myxml --函數true()和false()分別返回布爾值true和false,注意只要是非空那麼就將返回1select @ex.exist('true()') --返回1select @ex.exist('false()') --返回1select @ex.exist('/people') --返回1select @ex.exist('/people/haha') --返回0select @ex.exist('/people/student[@id[1]="201301"]') --返回1select @ex.exist('/people/student[@id[1]="201303"]') --返回0 --modify(XML_DML),使用此方法能夠修改xml數據內容 --參數XML_DML表示XML數據操做語言,它是一條字符串,最終將根據這個字符串來操做xml數據 --xml數據類型的modify方法只能在update語句的set字句中使用,注意若是是針對null值調用modify方法將返回錯誤 set @myxml.modify('replace value of (/people/student/@id)[1] with "201303" ') select @myxml --此時第一個student節點的id將被改成201303 --語法: nodes(QueryString) as table(column), 若是要將xml數據類型拆分爲關係數據,使用nodes方法將很是有效,它容許用戶將標識映射到新行的節點 --參數QueryString: 若是查詢表達式構造節點 --參數table(column): 結果行集的表名稱和列名稱 --得到全部student節點的數據,每一行顯示一條student節點的數據 select T.c.query('.') as result from @myxml.nodes('/people/student') as T(c) --將這些數據顯示爲一個表格 select T.c.value('(@id)[1]','int') as id, T.c.value('(./Name)[1]','nvarchar(16)') as name, T.c.value('(./Age)[1]','int') as age, T.c.value('(./Address)[1]','nvarchar(16)') as address from @myxml.nodes('/people/student') as T(c) --nodes的結果以下圖

 3.FOR XMLcode

  經過使用for xml字句,咱們能夠檢索系統中表的數據並自動生成xml格式。for xml在在SQL Server裏一共有4種模式:RAW、AUTO、EXPLICIT、PATH,for xml子句能夠用在頂級查詢和子查詢中,頂級for xml子句只能出如今select語句中,子查詢中的for xml子句能夠出如今insert、delete、update以及賦值語句中。xml

  raw和auto是比較簡單易理解的2種模式,raw模式將爲select語句所返回的查詢結果集中的每一行轉換爲帶有通用標記符<row>或可能提供元素名稱的xml元素。默認狀況下行集中非null的列都將映射爲<row>元素的一個屬性。這樣當使用select查詢時,會對結果集進行xml的轉換,它們將會被轉爲row元素的屬性。下面是關於for xml的sql介紹,raw模式是這4種模式裏最簡單的一種。auto模式也是返回xml數據,它與raw的區別在於返回的xml數據中,不是以raw做爲元素節點名,而是使用表名做爲元素名。這個是最明顯的區別,除此以外,auto模式的結果集還能夠造成簡單的層次關係。blog

select teacherId,teacherName from teacher where teacherSex='' for xml raw
--結果:<row teacherId="4" teacherName="謝一"/> -- <row teacherId="5" teacherName="羅二"/>
select teacherId,teacherName from teacher where teacherSex='' for xml auto --結果:<teacher teacherId="4" teacherName="謝一"/> -- <teacher teacherId="5" teacherName="羅二"/>

select student.id,student.name,teacher.teacherId,teacher.teacherName from student inner join teacher on student.teacherId=teacher.teacherId --結果: 10 小李 1 王靜 -- 11 小方 2 李四
select student.id,student.name,teacher.teacherId,teacher.teacherName 
from student inner join teacher on student.teacherId=teacher.teacherId for xml raw --結果: <row id="10" name="小李 " teacherId="1" teacherName="王靜" /> -- <row id="11" name="小方 " teacherId="2" teacherName="李四" /> select student.id,student.name,teacher.teacherId,teacher.teacherName
from student inner join teacher on student.teacherId=teacher.teacherId for xml auto /* 生成了嵌套關係 <student id="10" name="小李 "> <teacher teacherId="1" teacherName="王靜" /> </student> <student id="11" name="小方 "> <teacher teacherId="2" teacherName="李四" /> </student> */

   explicit比較很差理解,我本身感受學sql和學C#是兩個徹底不一樣的思惟,sql學起來是一種有點難受的感受,而C#就算鑽得很深也學得很爽。首先在數據這個角度看,使用explicit模式和使用auto模式都是相同的數據,並且也能夠獲得相同的層級效果。接下來就是explicit和auto最明顯的區別,也能夠說是explicit的優勢,使用explicit模式的時候咱們能夠以一種更加靈活的方式指定層級關係,而且能夠本身指定元素的名稱。固然explicit不像auto和raw那樣就方便的加在語句後面,若是你這樣作會報錯:"FOR XML EXPLICIT 要求第一列包含表明 XML 標記 ID 的正整數"或"FOR XML EXPLICIT要求第二列包含NULL或表明XML父標記ID的非負整數"。這2個錯誤中,若是第一列不爲整數類型那就會出現第一個錯誤,若第一列爲整數那就會出現第二個錯誤。總之,報錯的本質就是缺乏2列數據,這2列數據正是錯誤中提示的「包含表明XML標記ID的正整數」和「包含NULL或表明XML父標記ID的非負整數」。XML標記ID指的是層級關係中,這段select所指定的層級。XML父標記則是你這個select層級的父級元素ID,這樣根據標記ID和父標記ID來肯定這段結果在xml中的位置。

select distinct 1 as Tag, null as Parent, student.id as  [student!1!學生ID], student.name as [student!1!學生姓名], null as [teacher!2!老師ID], null as [teacher!2!老師姓名]
            from student,teacher where student.teacherId=teacher.teacherId union all
select 2 as Tag, 1 as parent, student.id, student.name, teacher.teacherId, teacher.teacherName from student,teacher where student.teacherId=teacher.teacherId

這是沒有加for xml的查詢結果,下面是添加了for xml explicit的sql和結果。

select distinct 1 as Tag, null as Parent, student.id as  [student!1!學生ID], student.name as [student!1!學生姓名], null as [teacher!2!老師ID], null as [teacher!2!老師姓名]
            from student,teacher where student.teacherId=teacher.teacherId union all
select 2 as Tag, 1 as parent, student.id, student.name, teacher.teacherId, teacher.teacherName from student,teacher where student.teacherId=teacher.teacherId for xml explicit

上面這段xml是根據第一個查詢結果集生成的,如今最關鍵的疑問就是如何根據這些數據生成xml?首先要深入的記住生成xml時是按照結果集中的順序一行一行開始生成的。因爲第一行Parent爲NULL也就是說第一行數據爲頂級元素,所以執行完第一條結果集的時候此時xml中的內容爲:

<student 學生ID="10" 學生姓名="小李 "     />

接下來執行第二條結果集,此時仍然是頂級元素,因而又生成了一條xml節點,此時xml中的內容爲:

<student 學生ID="10" 學生姓名="小李 " />
<student 學生ID="11" 學生姓名="小方 ">

接下來執行第三條結果集,如今Parent爲1,Tag爲2,說明這個結果集是Tag爲1的子元素,因而便生成了teacher節點而且在上一個student父節點中,接下來又是一個子節點因而最終的效果就是添加了2個teacher節點而且這2個節點在第二個student父節點中:

<student 學生ID="10" 學生姓名="小李 " />
<student 學生ID="11" 學生姓名="小方 ">
  <teacher 老師ID="1" 老師姓名="王靜" />
  <teacher 老師ID="2" 老師姓名="李四" />
</student>

可是,這個結果是錯誤的!由於是一個學生對應一個老師,而不是像顯示xml結果中一個student包含2個teacher節點,致使這個現象產生的最主要因素就是我前面提到的順序,xml的生成是根據結果集中的行數據一行一行生成的,爲此咱們須要在結果集上進行一個排序。要達到的效果應該是下面這樣的:

有了這個正確順序的結果集,那麼正確的xml就是這樣的:

<student 學生ID="10" 學生姓名="小李 ">
  <teacher 老師ID="1" 老師姓名="王靜" />
</student>
<student 學生ID="11" 學生姓名="小方 ">
  <teacher 老師ID="2" 老師姓名="李四" />
</student>

筆者是一個喜歡嚐鮮的人,很巧,課本和msdn上舉的都是2個層級的例子,學習過程當中在我學數據庫建的表上也是寫了2個select,因而我想嘗試3個層級,sql以下。

select distinct 1 as Tag, null as Parent, student.id as  [student!1!學生ID], student.name as [student!1!學生姓名], null as [teacher!2!老師ID], null as [teacher!2!老師姓名], null as [地址!3!老師地址]
            from student,teacher where student.teacherId=teacher.teacherId union all
select 2 as Tag, 1 as parent, student.id, student.name, teacher.teacherId, teacher.teacherName, null
            from student,teacher where student.teacherId=teacher.teacherId union all
select 3 as Tag, 2 as parent, null, null, null, null, teacher.teacherAddress from student,teacher where student.teacherId=teacher.teacherId order by [student!1!學生ID],[teacher!2!老師ID]
            for xml explicit

這樣寫是有點問題的,由於order by那個地方是直接用的上面的order by字句,xml中的順序應該會亂掉,但我沒想到一執行就報錯了。

錯誤提示意思是說我沒有打開父標記ID2,剛開始我真不明白這個打開是什麼意思,仔細揣摩錯誤提示後發現本質緣由了,下面是我不添加for xml的結果集,從中能夠看到第一條數據tag爲3,parent爲2,此時xml文檔中尚未生成ID爲2的父節點,也就是說生成xml時必須已經存在父節點才能夠繼續生成,不然會報錯。

因而我開始進行順序設置,但試了多種老是不能夠。讓咱們再來觀察這個結果集,若是要達到正確的效果那麼tag爲3的這2條數據應該是分別在2個tag爲2的下面,但是tag爲3的這一行只有地址這一列數據,不管怎麼進行order by這2行數據都是挨着一塊兒的,所以毫不可能達到正確的效果。

  不過這讓我對explicit的使用有一個更深入的體會,說實話它並非很好用,由於要獲得正確的效果必須得有一個正確的順序,而這個正確的順序不只僅體如今order by字句裏。在進行select時,頂級元素須要查找出部分數據,其他數據設置爲null,在進行二級select時,應該在頂級元素查找的基礎上增長二級元素所須要的,接着第三級元素應該在第二級元素的基礎上增長第三級所須要查找的元素。這麼作的緣由只有一個,就是進行order by時可以正確的排序,讀到這裏你是否已經想到如何設計才能讓上面的結果集是一個正確的順序呢?sql以下。

select distinct 1 as Tag, null as Parent, student.id as  [student!1!學生ID], student.name as [student!1!學生姓名], null as [teacher!2!老師ID], null as [teacher!2!老師姓名], null as [地址!3!老師地址]
            from student,teacher where student.teacherId=teacher.teacherId union all
select 2 as Tag, 1 as parent, student.id, student.name, teacher.teacherId, teacher.teacherName, null
            from student,teacher where student.teacherId=teacher.teacherId union all
select 3 as Tag, 2 as parent, student.id, student.name, teacher.teacherId, teacher.teacherName, teacher.teacherAddress from student,teacher where student.teacherId=teacher.teacherId order by [student!1!學生ID],[teacher!2!老師ID]
            for xml explicit

   explicit雖然功能更增強大,可是使用起來須要寫很長的sql而且還可能出錯。爲了解決這個問題四大模式中壓軸的path模式就要出場了,在path中列名很重要,由於列名或列別名將被做爲XPath表達式來處理,而這個表達式則指明瞭這一列在xml中位置。也就是說XPath表達式代替了explicit複雜的功能,只須要根據XPath中提供的類型、節點名稱和層次結構便可生成具備層次結構的xml數據。使用path模式會自動爲咱們生成的xml在原有頂級元素之上再添加一個頂級元素<row>,若是不想要這個頂級元素可在path中寫上空字符串便可。具體的實現就如文件夾路徑同樣,咱們可以使用「/」來進行層級的指定,在這一條字符串中的最後一個字符串中,若是僅僅只是字符串即未添加「@」那麼將會生成節點,若是添加「@」那麼則表示是屬性,固然關於path模式筆者也尚未研究完內容還有不少。具體sql以下,仍是實現上面的效果,可是使用的是極其簡單方便的path模式。

select student.id as  'student/@學生ID', student.name as 'student/@學生姓名', teacher.teacherId as 'student/teacher/@老師ID', teacher.teacherName as 'student/teacher/@老師姓名', teacher.teacherAddress as 'student/teacher/地址/@老師地址'
       from student,teacher where student.teacherId=teacher.teacherId for xml path('')
相關文章
相關標籤/搜索