XML查詢技術 XML文檔以一個純文本的形式存在,主要用於數據存儲。不但方便用戶讀取和使用,而且使修改和維護變得更容易。 XML數據類型 XML是SQL Server中內置的數據類型,可用於SQL語句或者作為存儲過程的參數。用戶可以直接在資料庫中存儲、查詢和管理XML文件。XML數據類型還能保存整個 ...
XML查詢技術
XML文檔以一個純文本的形式存在,主要用於數據存儲。不但方便用戶讀取和使用,而且使修改和維護變得更容易。
XML數據類型
XML是SQL Server中內置的數據類型,可用於SQL語句或者作為存儲過程的參數。用戶可以直接在資料庫中存儲、查詢和管理XML文件。XML數據類型還能保存整個XML文檔。XML數據類型和其他數據類型不存在根本上的差別,可以把它用在任何普通SQL數據類型可以使用的地方。
示例1:創建一個XML變數並用XML填充
DECLARE @doc XML
SELECT @doc='<Team name="Braves" />';
示例2:創建XML數據類型列
CREATE TABLE t1(
column1 INT,
column2 XML,
CONSTRAINT pk_column1 PRIMARY KEY(column1));
在上面的示例中,column2列是XML數據類型列。
示例3:不能將XML數據類型列設置為主鍵或外鍵
CREATE TABLE t1(
column1 INT,
column2 XML,
CONSTRAINT pk_column1 PRIMARY KEY(column2));
執行上面的代碼,報錯如下:
消息1919,級別16,狀態1,第1 行
表't1' 中的列'column2' 的類型不能用作索引中的鍵列。
消息1750,級別16,狀態0,第1 行
無法創建約束。請參閱前面的錯誤消息。
XML數據類型的使用限制
只有STRING數據類型才能轉換成XML。
XML列不能應用於GROUP BY語句中
XML數據類型存儲的數據不能超過2GB
XML數據類型欄位不能被設置成主鍵或者外鍵或稱為其一部分。
Sql_variant數據類型欄位的使用不能把XML數據類型作為種子類型。
XML列不能指定為唯一的。
COLLATE子句不能被使用在XML列上。
存儲在資料庫中的XML僅支持128級的層次。
表中最對只能擁有32個XML列。
XML列不能加入到規則中。
唯一可應用於XML列的內置標量函數是ISNULL和COALESCE。
具有XML數據類型列的表不能有一個超過15列的主鍵。
類型化的XML和非類型化的XML
可以創建xml類型的變數,參數和列,或者將XML架構集合和xml類型的變數、參數或列關聯,這種情況下,xml數據類型實例稱之為類型化xml實例。否則XML實例稱為非類型化的實例。
XML數據類型方法
XML數據類型共有5種方法
query():執行一個XML查詢並返回查詢結果(返回一個XML數據類型)。
示例4:
DECLARE @xmlDoc XML--聲明XML類型的變數@xmlDoc
SET @xmlDoc='<students>
<class name="數學" NO="8501">
<student>
<name>李林</name>
<sex>男</sex>
<age>16</age>
<address>江蘇</address>
</student>
</class>
</students>'--將XML實例分配給變數@xmlDoc
SELECT @xmlDoc.query('/students/class/student') AS test
--用query()查詢@xmlDoc變數實例中標簽<student>的子元素
查詢結果如圖所示
點擊查詢結果
如想查詢標簽
DECLARE @addr XML--聲明一個XML類型變數@addr
SET @addr='/students/class/student'
SELECT @addr.exist('/students/class="江蘇"') AS 返回值
結果如圖所示
註:exsit()方法的參數不必做精確定位
Value():計算一個查詢並從XML中返回一個簡單的值(只能返回單個值,且該值為非XML數據類型)。
Value()方法有2個參數XQuery和SQLType,XQuery參數表示命令要從XML實例內部查詢數據的具體位置,SQLType參數表示value()方法返回的值的首選數據類型。
示例6
DECLARE @xmlDoc XML--聲明XML類型的變數@xmlDoc
DECLARE @classID INT--聲明INT類型的變數@classID
SET @xmlDoc='<students>
<class name="數學" NO="8501">
<student>
<name>李林</name>
<sex>男</sex>
<age>16</age>
<address>江蘇</address>
</student>
</class>
</students>'--將XML實例分配給變數@xmlDoc
SET @[email protected]('(/students/class/@NO)[1]','INT')
--將value()方法返回值賦值給變數@classID
SELECT @classID AS classID
查詢結果如圖所示
註:SQLType不能是XML數據類型,公共語言運行時(CLR)用戶定義類型,image,text,ntext或sql_variant數據類型,但可以是用戶自定義數據類型SQL。
Modify():在XML文檔的適當位置執行一個修改操作。它的參數XML_DML代表一串字元串,根據此字元串表達式來更新XML文檔的內容。
示例7:在@xmlDoc的實例中,元素
DECLARE @xmlDoc XML--聲明XML類型的變數@xmlDoc
SET @xmlDoc='<students>
<class name="數學" NO="8501">
<student>
<name>李林</name>
<sex>男</sex>
<age>16</age>
<address>江蘇</address>
</student>
</class>
</students>'
SELECT @xmlDoc AS '插入節點前信息'
SET @xmlDoc.modify('insert <學歷>本科</學歷> after (students/class/student/age)[1]')
SELECT @xmlDoc AS '插入節點後信息'
查詢結果插入節點後信息如圖所示
註:modify()方法的參數中insert和其他關鍵字必須小寫,否則會報錯
Nodes():允許把XML分解到一個表結構中。此方法將XML數據類型實例拆分為關係數據,並返回包含原始XML數據的行集。
示例8:依然用@locat參數的實例來示範
DECLARE @locat XML--聲明XML變數@locat
SET @locat=
'<root>
<location locationID="8">
<step>8的步驟</step>
<step>8的步驟</step>
<step>8的步驟</step>
</location>
<location locationID="9">
<step>9的步驟</step>
<step>9的步驟</step>
<step>9的步驟</step>
</location>
<location locationID="10">
<step>10的步驟</step>
<step>10的步驟</step>
<step>10的步驟</step>
</location>
<location locationID="11">
<step>11的步驟</step>
<step>11的步驟</step>
<step>11的步驟</step>
</location>
</root>'--@locat變數的實例
SELECT T.Loc.query('.') AS result
FROM @locat.nodes('/root/location') T(Loc)
GO
查詢結果如下圖所示
XQuery簡介
XQuery是一種查詢語言,可以查詢結構化或者半結構化的數據。SQL Server 2008中對XML數據類型提供了支持,可以存儲XML文檔,然後使用XQuery語言進行查詢。
FOR XML子句
通過在SELECT語句中使用FOR XML子句可以把資料庫表中的數據檢索出來並生成XML格式。SQL Server 2008支持FOR XML的四種模式,分別是RAW模式,AUTO模式,EXPLICIT模式和PATH模式。
FOR XML RAW
將表轉換成元素名稱是row,屬性名稱為列名或者列的別名。
示例9:將Student表轉換為XML格式(FOR XML RAW)
Student表的數據如圖所示
執行語句:
SELECT * FROM Student FOR XML RAW;
查詢結果如圖所示
FOR XML AUTO
使用表名稱作為元素名稱,使用列名稱作為屬性名稱,SELECT關鍵字後面列的順序用於XML文檔的層次。
示例10:將Student表轉換為XML格式(FOR XML AUTO)
執行語句:
SELECT * FROM Student FOR XML AUTO;
查詢結果如圖所示
FOR XML EXPLICIT
允許用戶顯式地定義XML樹的形狀,不受AUTO模式中的種種限制。不能將FOR XML EXPLICIT直接用在SELECT子句中。
示例11:將xmlTest表轉換為XML格式(FOR XML EXPLICIT)
XmlTest表的數據如圖所示
SELECT DISTINCT 1 AS TAG,--指定頂級層級序號1
NULL AS PARENT,--該層級沒有父級
NULL AS '班級信息!1!',
NULL AS '班級信息!2!班級',
NULL AS '班級信息!2!班級類型',
NULL AS '班級信息!2!班主任',
NULL AS '學生信息!3!學號!Element',
NULL AS '學生信息!3!學生姓名!Element',
NULL AS '學生信息!3!性別!Element',
NULL AS '學生信息!3!總分!Element'--設置所有層級元素和屬性命名,暫時不對這些元素賦值
--例如在“學生信息!3!總分!Element”格式中,學生信息是元素名,3表示該元素所處層級,總分表示屬性名
--Element指出生成以屬性單獨為一行的XML格式
UNION ALL--層級之間用UNION ALL相連
SELECT DISTINCT 2 AS TAG,--指定二級層級序號2
1 AS PARENT,--父級序號是序號為1的層級
NULL,--在層級的代碼中已列出了所有層級元素和屬性命名,因此這裡給元素和屬性做賦值。這句語句對應層級代碼中“NULL AS '班級信息!1!'”,說明我希望該元素作為獨立成行的標簽,沒有賦值。
班級,--對層級中的“NULL AS '班級信息!2!班級'”賦值,將xmlTest表中的班級賦值給屬性班級
班級類型,--對層級中的“NULL AS '班級信息!2!班級類型'”賦值,將xmlTest表中的班級賦值給屬性班級類型
班主任,--同上
NULL,--這句語句開始對應的是層級的屬性,因此在層級的代碼中不做賦值,在下麵層級的代碼中做賦值
NULL,
NULL,
NULL
FROM xmlTest--指出上面賦值的數據源來自於xmlTest表
UNION ALL--各個層級之間用UNION ALL連接
SELECT 3 AS TAG,--指定3級層級序號3
2 AS PARENT,--父級是序號為2的層級
NULL,--對應層級的”NULL AS '班級信息!1!'“語句,不希望它有值,所以不做賦值
NULL,--這三個NULL對應層級的各個屬性,在層級的代碼中已經做過賦值,因此在這裡不做賦值
NULL,
NULL,
學號,--對應層級1代碼中的層級3屬性,在層級代碼3中進行賦值
學生姓名,
性別,
年級總分
FROM xmlTest
FOR XML EXPLICIT;--將上述查詢轉換為XML,不能漏掉,否則結果會以表格形式顯示
查詢結果如圖所示
在結果圖中我們發現,紅框中3個班級信息列在一起,而所有學生都列在高一3班下,這不是我們想要的結果,我們希望每個班級對應自己的學生。那麼如何解決此類問題呢,這涉及到排序。
註:如果層級中有多個數據完全重覆,可以在該層級對應的代碼前加DISTINCT關鍵字去除重覆元素。
首先刪除代碼行末的FOR XML EXPLICIT語句,僅僅執行剩下的部分,使結果以表格形式呈現,那麼結果如下
這個表格每行的順序也代表了該表格轉化為XML文檔後內容顯示順序。圖中層級2(TAG=2)的幾行,位置都在一起,這也就是為什麼層級3的所有數據都在高一3班下麵了。我們需要對錶格每行的順序進行調整,使學生所在行按照xmlTest表中的數據邏輯分散在班級行之下。但是根據上面的表格發現,不管按照什麼欄位排序,都不可能達到效果。
正確代碼如下
SELECT DISTINCT 1 AS TAG,
NULL AS PARENT,
NULL AS '班級信息!1!',
NULL AS '班級信息!2!班級',
NULL AS '班級信息!2!班級類型',
NULL AS '班級信息!2!班主任',
NULL AS '學生信息!3!學號!Element',
NULL AS '學生信息!3!學生姓名!Element',
NULL AS '學生信息!3!性別!Element',
NULL AS '學生信息!3!總分!Element'
UNION ALL
SELECT DISTINCT 2 AS TAG,
1 AS PARENT,
NULL,
班級,
班級類型,
班主任,
NULL,
NULL,
NULL,
NULL
FROM xmlTest
UNION ALL
SELECT 3 AS TAG,
2 AS PARENT,
NULL,
班級,
班級類型,
班主任,
學號,
學生姓名,
性別,
年級總分
FROM xmlTest
ORDER BY [班級信息!2!班級],[學生信息!3!學號!Element]
FOR XML EXPLICIT;
對比第一次代碼,我們發現上面的代碼不止在行末對數據按元素屬性進行了排序,還在賦值的代碼中有所改動。在層級1代碼中完全沒有改動,因為層級1的代碼作用是設置XML格式的,對數據排序沒有影響。在下麵幾個層級的賦值部分,每個層級的代碼中都對上面幾個層級的元素重覆賦值,這樣做使結果的表格中不再有那麼多屬性值是NULL,可以方便排序。最後再按照元素[班級信息!2!班級]和[學生信息!3!學號!Element]排序。讓我們看看結果如何。
運行上面的代碼,但不運行FOR XML EXPLICIT語句,看看表格中數據內容和行順序是否改變
如圖所示,發現行數據和學生數據的順序顯示正確。運行所有代碼得到XML文檔,結果如圖所示
由於XML文檔內容過長,不貼圖了,直接複製所有XML內容展示一下。
<班級信息>
<班級信息 班級="高一1班" 班級類型="創新班" 班主任="李玉虎">
<學生信息>
<學號>20180101</學號>
<學生姓名>李華</學生姓名>
<性別>男</性別>
<總分>5.680000000000000e+002</總分>
</學生信息>
<學生信息>
<學號>20180103</學號>
<學生姓名>孫麗</學生姓名>
<性別>女</性別>
<總分>3.390000000000000e+002</總分>
</學生信息>
<學生信息>
<學號>20180108</學號>
<學生姓名>吳偉</學生姓名>
<性別>男</性別>
<總分>5.280000000000000e+002</總分>
</學生信息>
</班級信息>
<班級信息 班級="高一2班" 班級類型="重點班" 班主任="薑傑">
<學生信息>
<學號>20180102</學號>
<學生姓名>張三</學生姓名>
<性別>男</性別>
<總分>6.270000000000000e+002</總分>
</學生信息>
<學生信息>
<學號>20180104</學號>
<學生姓名>袁康</學生姓名>
<性別>男</性別>
<總分>4.820000000000000e+002</總分>
</學生信息>
<學生信息>
<學號>20180106</學號>
<學生姓名>趙四</學生姓名>
<性別>男</性別>
<總分>5.680000000000000e+002</總分>
</學生信息>
</班級信息>
<班級信息 班級="高一3班" 班級類型="提高班" 班主任="師從光">
<學生信息>
<學號>20180105</學號>
<學生姓名>王婷</學生姓名>
<性別>女</性別>
<總分>7.610000000000000e+002</總分>
</學生信息>
<學生信息>
<學號>20180107</學號>
<學生姓名>周其</學生姓名>
<性別>女</性別>
<總分>3.480000000000000e+002</總分>
</學生信息>
<學生信息>
<學號>20180109</學號>
<學生姓名>甄誠</學生姓名>
<性別>女</性別>
<總分>7.020000000000000e+002</總分>
</學生信息>
</班級信息>
</班級信息>
將上面的結果對比一下原始xmlTest表,看看每個班級和它下屬學生的層級關係是否有誤。
註:寫FOR XML EXPLICIT代碼要註意,層級1的代碼中先設置層級結構,不要先急著賦值。在下屬層級的代碼中對層級1中的代碼進行賦值,最好重覆賦值,不然就會出現文中的排序問題。如果某個層級出現重覆數據,在該層級的代碼前加DISTINCT關鍵字。解決排序問題最好的辦法是對各個層級的屬性重覆賦值併在末尾用ORDER BY按層級屬性排序。
仔細觀察上面的XML文檔,發現總分屬性的值是個float類型,要把它轉換成int,只需要把層級3中對總分的賦值代碼改成CAST(年級總分 AS int)
FOR XML PATH
PATH模式提供了一種較簡單的方法來混合元素及屬性。在PATH模式中,列名或列別名被作為XPATH表達式來處理,這些表達式指定瞭如何將值映射到XML中。預設情況下,PATH模式為每一樣自動生成
沒有名稱的列
下麵介紹一種簡單的FOR XML PATH應用方式
SELECT 2+3 FOR XML PATH;--將2+3的值轉換成xml格式
查詢結果如圖所示
註:如果提供了空字元串FOR XML PATH(‘’)則不會生成任何元素。
SELECT 2+3 FOR XML PATH('');--將2+3的值轉換成xml格式並去掉<row>
查詢結果如圖所示
示例12:利用xmlTest表和mainTeacher表查詢出xmlTest表中成績>=700分的學生的班主任信息和學生信息,並轉化成XML格式
XmlTest表數據如下圖所示
MainTeacher表數據如下圖所示
執行下麵的語句
SELECT xmlTest.學號 AS '學生信息/@學號',--@符號表示該名稱為屬性名,斜杠表示子層級
xmlTest.學生姓名 AS '學生信息/@姓名',
xmlTest.班級 AS '學生信息/@班級',
mainTeacher.姓名 AS '學生信息/班主任信息/姓名',
mainTeacher.教師編號 AS '學生信息/班主任信息/教師編號',
mainTeacher.性別 AS '學生信息/班主任信息/性別',
mainTeacher.年齡 AS '學生信息/班主任信息/年齡',
mainTeacher.聯繫電話 AS '學生信息/班主任信息/聯繫電話'
FROM xmlTest,mainTeacher
WHERE xmlTest.年級總分>=700
AND xmlTest.班主任=mainTeacher.姓名
FOR XML PATH('result');--將根目錄名改為result
查詢結果如下所示
<result>
<學生信息 學號="20180105" 姓名="王婷" 班級="高一3班">
<班主任信息>
<姓名>師從光</姓名>
<教師編號>83928182</教師編號>
<性別>男</性別>
<年齡>28</年齡>
<聯繫電話>15963002120</聯繫電話>
</班主任信息>
</學生信息>
</result>
<result>
<學生信息 學號="20180109" 姓名="甄誠" 班級="高一3班">
<班主任信息>
<姓名>師從光</姓名>
<教師編號>83928182</教師編號>
<性別>男</性別>
<年齡>28</年齡>
<聯繫電話>15963002120</聯繫電話>
</班主任信息>
</學生信息>
</result>
TYPE命令
SQL Server支持TYPE命令將FOR XML的查詢結果作為XML數據類型返回。
示例13:依然是上面的例子,將查詢結果作為XML數據類型返回。
CREATE TABLE xmlType(xml_col XML);
--首先創建一個表xmlType,只有一列xml數據類型的xml_col
INSERT INTO xmlType
SELECT(--將上面的查詢語句全部複製到括弧中,末尾加上TYPE,表示將XML文檔作為xml數據類型,並插入到表xmlType中
SELECT xmlTest.學號 AS '學生信息/@學號',
xmlTest.學生姓名 AS '學生信息/@姓名',
xmlTest.班級 AS '學生信息/@班級',
mainTeacher.姓名 AS '學生信息/班主任信息/姓名',
mainTeacher.教師編號 AS '學生信息/班主任信息/教師編號',
mainTeacher.性別 AS '學生信息/班主任信息/性別',
mainTeacher.年齡 AS '學生信息/班主任信息/年齡',
mainTeacher.聯繫電話 AS '學生信息/班主任信息/聯繫電話'
FROM xmlTest,mainTeacher
WHERE xmlTest.年級總分>=700
AND xmlTest.班主任=mainTeacher.姓名
FOR XML PATH('result'),TYPE
);
SELECT * FROM xmlType;--查詢xmlType表
查詢結果如圖所示
雙擊打開查看XML
<result>
<學生信息 學號="20180105" 姓名="王婷" 班級="高一3班">
<班主任信息>
<姓名>師從光</姓名>
<教師編號>83928182</教師編號>
<性別>男</性別>
<年齡>28</年齡>
<聯繫電話>15963002120</聯繫電話>
</班主任信息>
</學生信息>
</result>
<result>
<學生信息 學號="20180109" 姓名="甄誠" 班級="高一3班">
<班主任信息>
<姓名>師從光</姓名>
<教師編號>83928182</教師編號>
<性別>男</性別>
<年齡>28</年齡>
<聯繫電話>15963002120</聯繫電話>
</班主任信息>
</學生信息>
</result>
FOR XML的嵌套查詢
示例14:在示例12的查詢結果中查詢班主任聯繫電話
SELECT (
SELECT xmlTest.學號 AS '學生信息/@學號',
xmlTest.學生姓名 AS '學生信息/@姓名',
xmlTest.班級 AS '學生信息/@班級',
mainTeacher.姓名 AS '學生信息/班主任信息/姓名',
mainTeacher.教師編號 AS '學生信息/班主任信息/教師編號',
mainTeacher.性別 AS '學生信息/班主任信息/性別',
mainTeacher.年齡 AS '學生信息/班主任信息/年齡',
mainTeacher.聯繫電話 AS '學生信息/班主任信息/聯繫電話'
FROM xmlTest,mainTeacher
WHERE xmlTest.年級總分>=700
AND xmlTest.班主任=mainTeacher.姓名
FOR XML PATH('result'),TYPE).query('result/學生信息/班主任信息/聯繫電話') AS '優秀教師聯繫方式';
SELECT裡面依然套用了示例13中被套用的代碼,外面用了query方法,查詢結果如下圖所示
<聯繫電話>15963002120</聯繫電話>
<聯繫電話>15963002120</聯繫電話>
XML索引
由於XML數據類型最大可存儲2GB的數據,因此需要創建XML索引來優化查詢性能。
主XML索引
主XML索引對XML列中XML實例內的所有標記,值和路徑進行索引。創建主XML索引時,相應XML列所在的表必須對該表的主鍵創建了聚集索引。
輔助XML索引
為了增強主XML索引的性能,可以創建輔助XML索引。只有創建了主XML索引後才能創建輔助XML索引。輔助XML索引分3種:PATH,VALUES和PROPERTY輔助XML索引。
創建索引
為表中某個列創建索引,要求該列是XML數據類型。
ALTER TABLE Student
ADD xml_test XML;--對Student表添加一個XML數據類型欄位xml_test
--對Student表的xml_test欄位創建主XML索引,命名為學生信息表
CREATE PRIMARY XML INDEX 學生信息表
ON Student(xml_test)
GO
--對Student表的xml_test欄位創建PATH輔助XML索引,記得寫上主索引名
CREATE XML INDEX 輔助學生信息表
ON Student(xml_test)
USING XML INDEX 學生信息表 FOR PATH
GO
註:輔助索引的命名不能與主索引相同。
修改和刪除索引(ALTER INDEX 和 DROP INDEX)
ALTER INDEX ALL ON Student--重建所有索引
REBUILD WITH(FILLFACTOR=80,SORT_IN_TEMPDB=ON,STATISTICS_NORECOMPUTE=ON);
--刪除索引
DROP INDEX 學生信息表 ON Student
GO
註:刪除主索引,與其相關的所有輔助索引也會被刪除。因此上面語句中刪除學生信息表索引後,輔助學生信息表索引也被刪除了。
OPENXML函數
OPENXML是一個行集函數,用於檢索XML文檔。在試用OPENXML函數之前,一定要先用系統存儲過程sp_xml_preparedocument分析文檔,該存儲過程在分析完XML文檔後會返回一個句柄,使用OPENXML檢索文檔時要將該句柄作為參數傳給OPENXML。
示例15
--定義兩個變數@Student和@StudentInfo
DECLARE @Student int
DECLARE @StudentInfo xml
--使用SET為@StudentInfo賦值
SET @StudentInfo='
<row>
<姓名>祝紅濤</姓名>
<班級編號>2019382910</班級編號>
<成績>89</成績>
<籍貫>沈陽</籍貫>
</row>
'
--使用系統存儲過程sp_xml_preparedocument分析由@Student變數表示的XML文檔,將分析得到的句柄賦值給@Student變數
EXEC sp_xml_preparedocument @Student OUTPUT,@StudentInfo
--在SELECT語句中使用OPENXML函數返回行集中的指定數據
SELECT * FROM OPENXML(@Student,'/row',2)
WITH(
姓名 varchar(8),
班級編號 varchar(10),
成績 int,
籍貫 varchar(20)
);
結果如圖所示
在上述語句中,sp_xml_preparedocument存儲過程語句用了2個參數,其中@Student是一個int型變數,該存儲過程會將句柄存儲在@Student變數中作為結果數據,@StudentInfo是一個XML類型的變數,存儲了將要進行分析的XML文檔。
OPENXML函數的語句中,使用了3個參數,其中@Student代表已經經過sp_xml_preparedocument存儲過程分析的文檔的句柄,’/row’使用XPath模式提供了一個路徑,代表要返回XML文檔中該路徑下的數據行,2是一個可選數據參數,表示將這些數據行以元素為中心映射。