这一节开始将介绍专用于XML数据的标准查询XQuery。XQuery是W< xmlnamespace prefix ="st1" ns ="urn:schemas-microsoft-com:office:smarttags" />3C组织为操作XML数据内容而专门指定的一套查询XML的标准。在SQL Server 2005中包含了对XQuery查询子集的支持,同时做了一些扩展。
XQuery对于XML数据来说,其作用就像SQL语言对于数据库中的数据一样。XQuery查询的基础表达式是XPath。< xmlnamespace prefix ="o" ns ="urn:schemas-microsoft-com:office:office" />
下面是一个XML数据。
心的距离
梅闻花
下面的SQL片段表示从上面的XML数据中查询出这本书的作者。
Select @Book.query('data(/Book/Author)')
说明:上面这段代码中,假设前面关于图书的XML数据被保存在XML类型的变量@Book中。其中的query函数必须为小写形式。因为query函数的参数部分是属于XQuery的查询表达式,所以是严格区分大小写的。
上面的查询直接返回作者的名称“梅闻花”。由此可以看出XQuery帮助查询到了XML数据内部的元素,这就是XQuery带来的最大好处。
在这个例子中query函数中的字符串参数部分是一个被称作XPath的表达式。在XQuery中将主要以XPath为查询表达式的基础和核心部分。要想比较深入的理解XQuery,就要首先深入理解XPath。
为了在T-SQL中支持XQuery的表达式,并和T-SQL的表达式相区别,SQL Server 2005专门为XML类型的变量、列等提供了如下表的函数。这些函数用“.”连接在XML列、变量等名称的后面,表示对“.”前的列、变量等执行XQuery查询。
表11-1 T-SQL中的XML类型的函数及含义
函数 |
含义 |
query() |
查询XQuery表达式,并返回查询结果 |
exist() |
判定是否有满足XQuery表达式的节点,有返回1,无返回0 |
modify() |
对XML数据进行插入节点、删除节点、替换节点等操作 |
value() |
比较XML类型值和SQL Server类型值 |
nodes() |
拆分XML节点转换成二维结果集的形式 |
注意:本书中将不介绍value函数和nodes函数。有关它们的信息可以查看SQL Server 2005的联机丛书。
XPath是一种用于能够在XML数据中定位和查找信息元素的语言。它也是W3C推荐的一个开放的标准。它能够用于查询XML数据内的任意元素。同时它还包含一个标准的函数库。像前一节例子中的“data”函数就是一个XPath函数。下面将通过例子详细介绍XPath。
先看一个XML数据。
在上面的XML数据中描述了两本书。现在想用一个简单的表示法来定位其中第二本书,该怎么做呢?
前面在讨论XML时说过,XML数据本是一个树形的结构。操作系统的文件目录也是一个树形结构,表示文件时,定义了一套路径表示法。类似于此,对于XML中元素的位置的描述也定义了一套类似的路径表示法。
下面的XPath示例给出了上面XML数据中第二本书的路径描述。
/Books/Book[2]
类似于树结构的术语,XPath表达式中也有父亲节点、孩子节点等的概念。下面的代码中的注释说明了这些概念。
王五
李某
了解了XPath中的基本术语,就可以方便的理解XPath的基本表达式。下表给出了一个基本表达式的种类及对应含义和示例。
表11-2 XPath基本表达式及含义
表达式 |
含义 |
示例 |
说明 |
Node Name |
选取此名称表示的节点的所有子节点 |
Factory |
选取Factory节点的所有子节点 |
/ |
从根节点选取 |
/Factory |
从根节点Factory选取所有子孙节点 |
// |
从任意一个位置开始选取到这个节点,而不管层次 |
//Worker |
从任意位置开始选取所有名为Worker标记的子标记 |
@ |
选取属性 |
@Pages |
选取有Pages属性的标记 |
. |
当前节点 |
/. |
根节点本身 |
.. |
当前节点的父节点 |
//Book/.. |
选择含有Book标记的所有子节点,包含Book节点本身 |
说明:表中的“.”和“..”在功能含义上类似于DOS系统下的文件夹下的两个同名文件。
通过表11-1可以看出,XPath表达式与常见的操作系统路径表达式非常相似。这也是XPath比较好学习好理解的一个方面。需要注意的是XPath的表达能力要远比操作系统的路径表达式在功能上强大的多。同时XPath中使用的是正斜杠“/”。
除了上面这些基本的XPath路径表达式外,XPath中另一个重要的概念就是“轴”。下面的列表显示了轴的基本表达式及含义。
表11-3 XPath轴表达式及含义
轴 |
含义 |
child |
返回节点的子节点 |
descendant |
返回节点的所有子孙节点 |
parent |
返回节点的父节点 |
attribute |
返回节点的属性 |
self |
返回当前节点 |
descendant-or-self |
返回当前节点及所有子孙节点 |
以上轴表达式的用法是轴加两个冒号然后跟上节点名称。下面的例子显示了轴表达式。
--返回根节点下的Book节点
/child::Book
--返回Book节点的父节点
//parent::Book
如果对W3C中的XPath标准比较了解,就会发现上面的XPath轴表达式只是标准中的一个子集。这就是SQL Server 2005中声称的只支持W3C标准XQuery的一个子集的结果。现在还无法在SQL Server 2005中使用全部的标准XQuery查询以及全部标准XPath表达式。在使用SQL Server 2005时请以SQL Server 2005联机丛书中相关的说明为准。本书在介绍这些内容时也是以SQL Server 2005联机丛书中的描述为准,而不以W3C的XQuery标准及XPath标准为准。
在操作系统的文件路径表达式中通常有一类称作通配符的符号,用于表示一类文件。在XPath中也有相对应的通配符,用于表示某一类XML内部节点。下表详细列举了XPath表达式中的通配符及含义。
表11-3 XPath通配符及含义
通配符 |
含义 |
* |
任意节点 |
@* |
任意属性节点 |
node() |
任意类型的节点 |
下面的例子显示了如何在XPath中使用通配符。
--选取Book节点下的任意类型的节点
/Book//node()
--选取所有带有属性的Book节点
//Book[@*]
--选取根节点下所有的节点
/*
通过灵活组合使用上面所有的XPath表达式的元素,就可以方便的定位到任意XML数据内的任意节点或一类节点处。这就是XPath语言被设计得初衷。
与文件目录树形结构不同的是XML内部的节点往往可以重复出现,这时使用简单的XPath路径表达式往往无法精确地定位到其中某一个节点上。这时就需要使用称作XPath谓词的表达式来区分这些节点。 它是建立在XPath基本表达式的基础之上的。
XPath谓词是被放在XPath表达式中某节点后的中括号“[]”中的一个简单条件表达式。下面的XML数据中有若干个名为Worker的节点。
如果要查找其中Pay属性大于1200的Worker时可以使用下面的带谓词的XPath表达式。
/Factory/Worker[@Pay>1200]
而下面的谓词表达式则表示返回带有Name属性的Worker节点。
/Factory/Worker[@Name]
下面的列表显示了可以在XPath谓词中使用的运算符及含义的列表。
表11-4 XPath谓词运算符及含义
运算符 |
含义 |
+ |
加 |
- |
减 |
* |
乘 |
div |
除 |
= |
等于 |
!= |
不等于 |
> |
大于 |
>= |
大于等于 |
< |
小于 |
<= |
小于等于 |
and |
并且 |
or |
或者 |
mod |
计算余数 |
说明:表中的单词必须为小写形式,因为XML是区分大小写的,XPath谓词继承了这一特性。其中的and和or两个运算符用于连接两个条件谓词。含义等同于T-SQL中的AND连接符和OR连接符。
FLWOR 是 for、let、where、order by 和 return 单词首字母的连接缩写。分别表示五类XQuery查询语句。在SQL Server 2005中不支持let语句,这里就不作介绍。
for语句表示一个循环迭代,下面的例子显示了如何使用for语句。
for $x in //book
说明:上面例子中$x表示循环变量,代表当前要处理的路径表达式//book的每一个子节点。在后面的语句中可以使用$x表示当前节点来遍历in后面的路径表达式下的每一个子节点。可以在表达式中将 $x视作一个节点即可。
下面的例子显示了如何组合使用for、where和return语句。并且介绍了每种语句的含义。
Declare @BookXML As XML
Set @BookXML =
'
'
Select @BookXML.query(
'
for $x in /Books/Book
where $x/@Pages>400
order by $x/@Name
return $x
'
)
上面的例子执行后返回如下的结果。
在前面的例子脚本中使用了for语句、where语句和return语句。$x变量表示满足路径表达式/Books/Book的子XML树。$x变量除了必须以“$”符号开头外,后面的名称无特殊要求,可以自行命名一个表达清晰的名称。循环迭代中一共有两个语句,where语句用于判断当前的Book信息的Pages属性是不是大于400,如果满足就用return语句返回$x。
其中where语句后面的表达式称为XQuery条件表达式,在下一节中将详细介绍XQuery条件表达式。
这里需要注意理解的是,虽然找到了满足条件的Book子XML数据信息,并且用return返回了这一结果,但是for语句的迭代并没有因此而结束。这与函数和存储过程的返回语句是不相同的。for语句会遍历所有满足in后面的路径表达式的XML数据节点,而不管后面的where语句和return语句怎么执行。这个特性可以从返回的结果看出来。
在有些情况下,需要对返回的XML子数据结果进行一个排序,这时就可以使用order by语句来完成这一功能。在order by语句中仍然要提供一个使用了迭代变量的XPath路径表达式。下面的例子显示了如何用order by进行结果的排序。
--此例中的@ BookXML变量的定义以及内容同前一例中完全相同,此处不再重复相同部分
--同时注意查询中的循环迭代变量被换成了$Book
Select @BookXML.query(
'
for $Book in /Books/Book
where $Book/@Pages>400
order by $Book/@Name
return $Book
'
)
这个例子返回的结果如下。
明显的看出这个结果用Name的属性值进行了排序。
下面的例子显示了一个稍微复杂点的FLWOR查询,以作为本小节的结束。
Declare @BookXML As XML
Set @BookXML =
'
'
Select @BookXML.query('
图书
{
for $book in /Books/Book
order by $book/@Name
return
}
')
这个例子中定义变量@BookXML的部分与前面的例子相同。在这个例子query函数的XQuery表达式中,引入了额外的标记。可以明显地看出这些标记是在定义一个HTML文档。特别要注意的是在HTML标记和FLWOR表达式交叉的地方使用了“{}”将XPath表达式括了起来,这是必须的,否则那个XPath表达式会被当作标记的一部分而被原样显示出来。可以将这个例子执行的结果保存到一个文本文件中,再将此文本文件扩展名改为html,然后双击打开看一下这个用XQuery生成的html文件。这也说明可以用XQuery查询直接构建WEB格式的网页。这也是XQuery被推荐的用途之一。
前面一节中主要介绍了XQuery的FLWOR语句。可以看出FLWOR语句主要是用于迭代表达式的。虽然其中也有用于判定条件的where语句,但是从语句的功能上来看它还是比较弱的。比如它没有提供如果where语句的条件不成立时的分支语句。
为了使条件判断能够处理有分支的情况,XQuery标准中还定义了if…then…else表达式用于较复杂的条件语句。
下面的例子显示了如何使用XQuery的条件表达式。
Declare @BookXML As XML
Set @BookXML =
'
'
Select @BookXML.query('
for $x in /Tools/Tool
return if ($x/@Price>10.00)
then
else
')
下面是以上查询运行后的结果。
从结果可以看出,if($x/@Price>10.00)表达式将价格高于10.00的工具都用主要工具
这就可以看出,if…then…else提供了一种条件选择,可以在条件不成立时也可以做一些事情,而不像where那样只有一个条件分支。
在W3C的XQuery标准中还没有关于如何管理XML数据内容的部分。而SQL Server 2005则按照类似SQL语句中的Insert、Delete等语句,扩展了这一部分,用于管理XML数据的内容。从而可以达到新增XML内部节点、删除内部节点、扩展XML层次等的目的。
这一小节先介绍insert语句。下面看一段例子。
Declare @BookXML As XML
Set @BookXML =
'
'
Select @BookXML
Set @BookXML.modify(
'
insert
as last
into /Works[1]
'
)
Select @BookXML
这个例子显示了如何将一个记录了工人信息的节点,插入到Works节点的最后。如果数据较多时,可以使用一个modify函数调用一条insert语句进行批量插入。下面是批量插入的例子。
Declare @BookXML As XML
Set @BookXML =
'
'
Select @BookXML
Set @BookXML.modify(
'
--注意下面节点数据后面用“,”进行了分离。
insert (
)
into /Works[1]
'
)
Select @BookXML
说明:在上面的例子中多个节点数据间使用了“,”进行分离,这是必须的。需要“,”情况是当要分离多个完整独立的XML节点数据时。如果XML数据有子节点就在顶级元素的结束标记后使用“,”。
使用modify函数的delete语句可以对XML数据内部的节点进行删除操作。下面的例子显示了如何删除XML数据的一个内部节点。
Declare @BookXML As XML
Set @BookXML =
'
'
Select @BookXML
--下面的语句将删除第二个工人的节点
Set @BookXML.modify(
'
delete /Works/Work[2]
'
)
Select @BookXML
删除语句还可以删除某个节点的某个属性。下面是删除属性的例子。
Declare @BookXML As XML
Set @BookXML =
'
'
Select @BookXML
--下面的语句将删除第二个工人节点的Pay属性
Set @BookXML.modify(
'
delete /Works/Work[2]/@Pay
'
)
Select @BookXML
从上面两个例子可以看出XQuery扩展中,delete语句是比较简单的,只要能够准确无误的写出XPath表达式,那么就可以删除任何想删除的XML节点。
类似于T-SQL中Update语句的功能,modify函数的replace value语句可以将指定节点值替换成另外一个值。
Declare @BookXML As XML
Set @BookXML =
'
'
Select @BookXML
--下面的语句将删除第二个工人节点的工资值替换成800.00
Set @BookXML.modify(
'
replace value of( /Works/Work/@Pay)[2]
with "800.00"
'
)
Select @BookXML
有时,在替换节点值的时候会根据节点值或属性的情况来决定替换成什么值。下面的例子显示了如何在replace value语句中使用if…then…else语句。
Declare @BookXML As XML
Set @BookXML =
'
'
Select @BookXML
--下面的语句将删除第一个工人节点的IsPay属性替换成否
Set @BookXML.modify(
'
replace value of(/Works/Work/@IsPay)[1]
with(
if(/Works/Work/@IsPay="0")
then "否"
else "是"
)
'
)
Select @BookXML
到此所有SQL Server 2005种扩展出来的XQuery的数据管理语句部分就介绍完了。大多数情况下,需要组合使用上面的三种数据管理语言,来对XML数据内部的结构进行修改操作。
在前面的大多数例子中,基于XQuery的查询大多数都是将XQuery作为结果的一部分。在SQL Server 2005种同样允许使用XQuery作为条件表达式的一部分。下面的例子显示了如何使用exist函数来判定某个节点是否存在,并作为where语句中条件的一部分。
--第一步使用前面创建的测试数据库
USE [MyTest1]
Go
--第二步如果MyXMLTb表不存在就创建该表
IF NOT EXISTS (SELECT * FROM sys.objects
WHERE object_id = OBJECT_ID(N'MyXMLTb')
AND type in (N'U'))
CREATE TABLE MyXMLTb(
XMLKID int IDENTITY(1,1) NOT NULL PRIMARY KEY,
MyXML xml NULL
)
Go
--第三步插入4行存储了学生信息的XML数据
Insert Into MyXMLTb(MyXML)
Values(
'
'
)
Go
Insert Into MyXMLTb(MyXML)
Values(
'
'
)
Go
Insert Into MyXMLTb(MyXML)
Values(
'
'
)
Go
Insert Into MyXMLTb(MyXML)
Values(
'
'
)
Go
--在Where子句中使用exist函数
Select *
From MyXMLTB
Where MyXML.exist('//Student[@Avg < 80]') = 1
上面的查询将返回结果。
2
这正是平均成绩小于80分的学生的成绩。
在讲解XML的时候提到过,XML数据实际上常常存储在文本文件中。这些文件常常被命名以扩展名xml。有时这些文件是非常庞大的。如何高速的将这些文件导入到数据库中就成为现实的问题。
下面的例子说明如何将一个存储于C盘根目录下的名为Factory.xml的XML文件导入到MyTest1数据库的MyXMLTB表中。
--导入XML文件到数据表的指定字段中
Insert Into MyXMLTb(MyXML)
Select *
From OpenRowset(
BULK 'C:\Factory.XML',SINGLE_BLOB)
As XMLFile
Go
Select *
From MyXMLTB
例子中用到了重要的T-SQL函数OpenRowset。在本书前面的章节中已有关于该函数的详细介绍,此处不再赘述。请参阅相关章节。
在一些应用中,需要将存储在数据库中的XML数据导出到一个指定的XML文件中,用于数据交换或分发的任务。下面的例子显示了如何做到这一点。
--打开命令行支持
Exec sp_configure 'xp_cmdshell',1
Go
RECONFIGURE WITH OVERRIDE
Go
--利用BCP命令行工具,从MyTest1数据库中的MyXMLTB表将数据导出
Exec xp_cmdshell 'bcp "Select MyXML From MyTest1.dbo.MyXMLTB" queryout C:\MyXML.XML -c -SRock -USA -P*#06#'
Go
--关闭命令行支持
Exec sp_configure 'xp_cmdshell',0
Go
RECONFIGURE WITH OVERRIDE
Go
上面的例子中实际上利用了 BCP 命令行工具导出了 XML 数据。关于这个命令前面有专门的章节介绍,请参阅相关章节。