转载--SQL Server 2005的XQuery介绍
原文地址:
http://bbs.51cto.com/thread-458009-1-1.html
引用:
摘要
本文介绍了SQL Server 2005能够支持的XQuery的各方面特性如FLWOR语句,XQuery的操作,if-then-else结构,XML的构造函数,XQuery的内置函数,类型的转换操作符,并举例说明了上述XQuery功能的应用和操作。本文同时也对SQL Server 2005所不能支持的XQuery的特性进行了分析讨论,并提出了实际工作中的解决方案。在本文的最后章节中,详细举例介绍了三种特定的XQuery应用场景。
引用:
内容目录
导言
SQL Server 2005中的XML数据类型
类型化vs.非类型化的XML数据类型
XML数据类型的方法
XQuery入门
XPath2.0简介
XQuery简介
XQuery的优点
XQuery的应用领域
在服务器端使用XQuery的优点
当使用XML Schemas时如何执行XQuery
XQuery表达式的结构
XPath 2.0表达式
FLWOR语句
For
Where
order by
return
FLWOR表达式vs. XPath表达式
XQuery的操作
数学运算符
比较运算符
普通比较运算符
数值比较运算符
节点比较运算符
节点顺序比较运算符
逻辑操作符
if-then-else语句
使用XQuery构造XML
XQuery构造器 vs. FOR XML语句
XQuery内置函数
数据访问
字符串的处理
聚集函数
上下文函数
关系类型表达式
类型声明表达式
如for语句中的xs:TYPE
类型检查表达式
xs:TYPE实例
类型转换函数
隐式类型转换
显式类型转换
值类型构造器
cast as xs:TYPE ?操作符
访问关系性的列和变量
不支持的特性和工作中的解决方案
最佳实践和指导方针
XML数据的修改
插入操作
删除操作符
Update操作符
XQuery应用场景
场景1:性能评价系统
场景2:病例系统
场景3:资产管理系统
结论
导言
XML是被用来作为一种文档格式而发展起来的。然而,XML所具有的其它特征,比如可扩展性、支持国际标准、表示结构化和半结构化数据的能力,以及人和机器对其简单的可读性,使得它成为一种广泛应用的、平台无关的数据表示格式。由于XML获得了广泛的认可,因此很多用户都用XML来解决复杂的商业问题,例如那些涉及到数据集成的问题。有许多案例都推荐将信息直接存储到XML文档,这比将信息先存到数据表中然后再将这些信息组成XML消息要有效的多。要查看更多关于这些案例的信息,可以查阅“微软SQLserver2005 XML最佳实践”白皮书中相关文档。XML在存储文档和半结构化数据表示方面的应用已经让XML发展成为一种可以在服务器端存储简单数据管理的数据存储格式。
可是这种发展也导致了一个问题-从XML数据中提取出来的信息要作为BLOBs类型存储到关系型数据库中,需要一种查询语言来提取XML所表达的信息,并使其符合关系型数据库中的要求。Microsoft SQL Server 2000提供了OpenXML,它可以被用来查询,从被设计时,就满足了从XML数据映射到关系型数据格式的要求,但它并没有完全支持XML数据模型(见表1)。关系数据模型和XML数据模型在很多方面都不一样。下表简单列举了两种数据模型的主要区别。
表1 关系型数据模型与XML数据模型之间的区别
不断增加处理越来越多样化的结构数据的需求和数据间隐含约束的必要性是扩展关系型数据模型以支持XML文档存储的最重要的两个原因。另外,SQL语言在处理半结构化或者标记信息时的局限性也促使了XQuery语言的发展。XQuery语言在设计的时候已经考虑到XML数据的特性和处理XML数据的相关问题。
SQL Server 2005通过内置XML数据类型支持XML数据的本地化存储。XQuery 1.0版本是经World Wide Web Consortium (W3C) XML查询工作组定义,基于XML数据的公式化查询语言。XQuery,像SQL一样,是一种声明性的查询语言,就如同我们在下面的文章中将会看到的一样,能通过基本的SQL和XPath知识来轻松地理解它。
本文基于XQuery 1.0在SQL Server 2005中的实践,同时也基于XQuery 1.0 2004年的工作草图。本文的第一部分提出了新的XML数据类型的总述以及它的相关特点,接下来的部分,分别地介绍XQuery的多样操作,XQuery的内置函数,关系型表达式,以及SQL Server 2005不支持的部分。最后,介绍最近的最佳应用和指导,XML数据的修正以及XQuery的应用事例的信息。
引用:
SQL Server 2005中的XML数据类型
SQL Server 2005引入的新XML数据类型使得用户具有在数据库中存储XML文档和段落的能力。一个XML数据类型可以用来创建列,用来创建存储过程或者存储函数中的参数,或用来创建变量。另外,用户可以关联一个XML类型的列和一个XML Schema集来创建一个新的类型化的XML列。集合中的XML Schema被用来验证和类型化XML实例。
类型化vs.非类型化的XML数据类型
XML数据类型与XML schema集相关联,为XML实例增加了Schema的强制性约束。如果XML数据被关联到一个XML Schema集合,它就被称为类型化的XML。否则,就叫做非类型化的XML。
SQL Server 2005中XML数据类型实现了ISO SQL-2003标准化XML数据类型。它不仅能够存储结构良好的XML1.0文档,也能存储根节点为文本的所谓的XML内容段落,也能存储包含任意数目根元素的内容。针对具有良好格式数据的检查已经完成,这些检查不必把XML数据类型绑定到XML Schemas。那些没有良好格式的数据不能被检查。
当Schema是不可知的时候,非类型化的XML就很有用。当Schema可知,但它的变化非常快以至于很难保持的时候,或者当存在多个Schema,最后的绑定取决于外部的需求时,非类型化的XML也很有用。另外,当XML Schema中包含数据库引擎不支持的XML Schema结构时,非类型化的XML也很有用。在这些情况下,你可以使用在公共语言运行时(CLR)用户自定义功能中的System.XML验证器来提供有效性验证。
如果在XML Schemas集中,你有XML Schemas来描述你的XML数据,你能够通过关联XML Schema集和XML列用来提供类型化的XML数据。XML Schema被用来验证数据的有效性;在编译查询或者数据编辑语句期间执行比非类型化XML更加精确的类型检查;或者被用来优化存储和查询的处理效率。
类型化的列,参数和变量能够存储XML文档(Document)或者内容段落(Content),在声明的时候,你可以通过开关指定你存储的类型(文档或者内容,默认的是内容)。另外,你必须同时提供一个XML Schema集。如果每个XML实例只有一个根元素,那么指定为文档类型,否则指定为内容类型。在静态类型推断期间,XQuery编译器使用文档(Document)标志信息来推断单个的根元素。
引用:
XML数据类型的方法
XML数据类型支持五种方法来操作XML实例。XML数据类型的方法描述如下:
query() 方法携带一个对若干个XML节点求值的XQuery表达式,允许用户查询一个XML文档中的段落。这个方法的返回值一个非类型化XML文件类型的值。
value() 方法是用来从一个XML文档数据值中提取关系型数值。这个方法携带一个用来识别单个XML节点的XQuery表达式并返回期望得到的SQL类型。XML节点的返回值被转换为指定的SQL类型。
exist()方法允许用户在XML文档中执行查询以确认一个XQuery表达式的结果是否为空。当XQuery表达式返回一个非空的结果时,这个方法的返回值为1;当表达式返回空时,方法返回值为0;当XML实例本身为NULL时,就返回NULL值。
使用XML数据类型的nodes()方法可以较容易的把一个XML文档分解为关系型数据。nodes()方法接受一个XQuery表达式,并返回一个行集(Rowset),行集中的每一行都描述一个由查询表达式所标识出来的上下文节点。XML数据类型的其他方法,如query(), value(),exist(),和 nodes(),都能调用nodes()方法返回的上下文节点。
modify()方法能用来对一个XML文档的内容进行修改。它支持XML DML语句,从而实现在一个XML实例中插入、更新或者删除一个或多个节点。当它遇到一个NULL值时,它会报错。
详细内容,查阅“微软SQLserver2005 XML最佳实践”白皮书。
XQuery入门
XQuery是一种用于XML查询的新语言,支持基于XPath2.0的数据浏览和采集。本章节为我们初步展示了XQuery语言各方面的内容,比如XQuery和XPath之间的关系,XQuery的优点,XQuery的应用领域,XQuery中XML Schema的规则等等。
XPath2.0简介
XPath1.0,是由W3C工作组定义,用于在单个XML文档中定位节点的语言。XPath1.0使用基于路径的规则来标识XML文档中的节点。同时它定义了XSLT 1.0和XPointer的核心规则。XPath 1.0有处理字符串、布尔值和浮点数的内置函数。它定义了根据设置的过滤标准来过滤节点的语法。XPath 1.0正在升级为XPath 2.0以支持更多的系统类型,提供更多的功能。XQuery 1.0基于对XPath 2.0,增加了排序、重装、构造功能,而且实现了XPath2.0未能实现的数据浏览和过滤方面的性能。
XQuery简介
XQuery是一个可以定义、类型化、函数化的语言,是XML查询组专为从XML格式中查询数据而设计的语言。XQuery具有与XML标准系列中XPath 2.0和XSLT 2.0一样的数据模型和XML Schema类型系统。设计XQuery用于处理非类型化XML文档(不包括关联数据的schema),类型化XML schema,或者两者的混合体。如同上文提到的,XQuery 1.0基本上是XPath2.0的扩展集。XQuery 1.0除了拥有XPath2.0的特点外,它还拥有下面的性能:
• 在FLWOR语句中增加了order by语句,它可以对文档数据进行重新排序。
• 在FLWOR语句中增加了let语句,它能够指定表达式的结果以实现更多的应用(SQL Server 2005中不支持)。
• 在查询语句的嵌缀部分,可以指定静态的上下文节点(比如命名空间的绑定前缀namespace prefix bindings)。
• 提供构造新节点的功能。
• 允许用户自定义函数(SQL Server 2005中不支持)。
• 能够创建modules/libraries(SQL Server 2005中不支持)。
XQuery的优点
• 当前的SQL和XPath知识是很容易学习的。
• 相比XSLT的查询语句,XQuery查询语句代码更简洁。
• 当XML数据是类型化的,那么XQuery是一个强类型语言,它能够通过避免非法的类型转换以及确认类型是否可以在查询操作中使用,来提高查询语句的执行效率。
• XQuery能当作弱类型语言使用,为非类型化数据提供更强的功能。SQL Server 2005提供了同时支持强类型和弱类型两种关系的静态类型。
• 因为XQuery执行查询需要的代码比XSLT少,所以它的执行效率也高。
• XQuery正在成为W3C工作组的推荐语言,同时它也将会被主流的数据库提供商所支持。
在本文中,关于在编写XQuery1.0语言时需要特别注意的地方:
• XQuery规范是基于当前开发环境进行阐述,将来可能会发生变化。而SQL Server 2005是W3C工作草图所实现的稳定部分,不会发生变化。
XQuery的应用领域
一般,XQuery的应用领域分类如下:
• 查询和分析数据:XQuery在查询大容量数据时表现出色,而且能够过滤、分类、排序以及转换需要的信息。典型的XML文档查询的应用包括描述半结构化信息,定义name-value包,分析日志,处理日志以及监控应用的日志来查找潜在的应用错误和安全方面的问题等等。
• XQuery的集成应用:当团队开始摒弃自己私有的集成应用方法,而开始采用标准的以集成应用为基础的方法时候,满足把单个应用中内部定义使用的数据转换为标准的可以格式化转换的数据的需求就成为头等重要的问题。因为XQuery能够构造并转换XML数据,所以XQuery就满足了这个需求。在集成应用领域,一个典型的XQuery应用是把本地使用的XML数据库/关系型数据资源的词汇表,翻译为另外一个应用者的本地XML数据库/关系型格式数据语言。
在服务器端使用XQuery的优点
对比起在客户端处理XML过程,在服务器端使用XQuery来处理XML过程要具备更多的优点。其中一些优点可以总结如下:
• 减少网络负载:在服务器端处理XML数据,只把结果传递到客户端,减少了网络负载。
• 更加安全:只有当使用客户端XML进程时才把客户端需要的数据传递到客户端,避免在网络上传输完整数据而带来的风险。
• 更易维护:在服务器段处理XML能使得浏览器独立于客户端代码,这能够更容易的维护客户端。
• 性能的改进:在服务器端使用XQuery写的查询语句可以使用SQL查询器进行优化。这样优化处理的性能要高于在客户端重新获得数据并进行数据过滤。此外,还可以通过为XML数据类型的列创建索引来得到更强大的性能。
当使用XML Schemas时如何执行XQuery
关联一个XML数据类型的XML schema集能被以下的关系引擎使用:
• 在插入操作中来实例化XML。
• 在修改操作中来实例化XML。
• 在进行最早的静态类型错误检查和改进查询性能时,XML schema中的类型信息被用来确定最佳的查询计划和避免很多运行时的检错。
• XML schema中描述的类型信息被SQL Server用来优化存储。
引用:
XQuery表达式的结构
SQL Server 2005中的一个XQuery表达式包括两个部分—前缀(prolog)和主体(body)。前缀能逐个声明包含的命名空间。命名空间的声明可以通过映射前缀和命名空间的URI,使你能够用前缀来替代查询体中的命名空间的URI。通过默认声明命名空间(declare default namespace)的声明,你不用绑定元素名称的默认命名空间就可以引用元素的名称。
XQuery表达式的主体包含了定义查询结果的查询表达式。比如,它可以是一个FLWOR表达式(参见本文章节的“FLWOR语句”),一个XPath 2.0 表达式(参见本文章节的“XPath 2.0表达式”),或者另外一个XQuery表达式比如一个构造或算术表达式。
实例:在XQuery头中声明默认的命名空间
下面是从所有职员中查找一个JobCandidateID等于3的应聘者。查询中只定义了一个默认命名空间,而没有使用任何一个命名空间前缀。
SELECT Resume.query('
declare default namespace "http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/Resume";
/Resume/Employment
') as Result
FROM [HumanResources].[JobCandidate]
WHERE JobCandidateID = 3
实例:使用“WITH XMLNAMESPACES”语句声明命名空间
SQL Server同样支持 SQL-2003的标准扩展,允许用户在SQL的每一个SQL查询要素上绑定XML命名空间,这样就可以避免重复声明同样的XML数据类型的方法。下面的查询显示了前面查询实例修改后的版本。这个查询使用WITH XMLNAMESPACES 语句声明了一个命名空间。
WITH XMLNAMESPACES( 'http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/Resume' AS "RES")
SELECT Resume.query('
/RES:Resume/RES:Employment
') as Result
FROM [HumanResources].[JobCandidate]
WHERE JobCandidateID = 3
XPath 2.0表达式
XQuery使用XPath2.0表达式查找一个文档中的节点,在一个文档中移动节点或者在文档间移动节点位置。定义移动路径的XPath是一个彼此分离的步骤组成的有序队列。每一个步骤包含一个axis,一个node test和多个步骤判断。
Axis说明了移动的方向,和上下文节点的关系。SQL Server2005中支持的axes是child, descendant, parent, attribute, self 以及descendant-or-self。
一个node test说明了步骤选择的这些节点所必需满足的条件。节点的条件可以基于节点名称或者节点类型来说明。
步骤判断可以用动作(predicates)或者重新引用(dereference)。一个动作就充当一个在相等框架下定义的一个节点队列中的节点过滤器;一个重新引用反映出一个节点队列中各节点的属性以及元素是否与参照节点的一致。作为重新引用的输入,该节点队列必须包含IDREF或者IDREFS类型的元素或者属性。重新引用可以产生一个新队列,其各节点ID类型的属性值都匹配取自输入队列中元素和属性的IDREF值。
XPath表达式的步骤是可以关联求值的。每个步骤执行时都为下一步设置了赋值的上下文项。Path表达式中的一个上下文项就是一个节点,它被选择作为XPath表达式中每个步骤执行的返回值。根据前一步所获得的上下文节点能够关联求得当前的步骤。XPath表达式执行完所有步骤的返回结果就将是一个根据path表达式排列的文档中节点的有序队列。
下面的表达式用AdventureWorks数据库中的表[HumanResources].[JobCandidate]来举例说明了path表达式的概念。下面的path表达式选择了address类型节点中值为Home的节点。
//child::ns:Addr.Type[.="Home"]/parent::node()
Path表达式:
• child 是axis的定义。
• :: 是 axis 的分隔符。
• ns 是命名空间前缀。
• Addr.Type 是 node test。
• [.=”Home”] 是一个指向上下文节点的动词表达式。
XQuery同样支持缩写语法来说明axis。下面的表格列举了axis以及相应的缩写语法。
表 2 axes缩写语法
示例:从历史职业中选择工作单位的名称
下面所示的XPath表达式选择了Emp.OrgName文本类型的,同时也是Resume/Employment子节点的节点。在这里,text()用来选择Emp.OrgName元素文本类型的子节点:
/Resume/Employment/Emp.OrgName/text()
引用:
FLWOR语句
FLWOR语句组成了XQuery表达式的主体,它与SQL的SELECT语句很相似。FLWOR(发音同“flower”)是FOR, LET, WHERE, ORDER BY, RETURN的缩写。XQuery的FLWOR表达式可以进行重复声明、变量绑定、过滤、排序以及返回结果的操作。SQL Server 2005中支持FOR, WHERE, ORDER BY和 RETURN:
For
FLWOR表达式中的for语句支持用户定义一个变量来遍历某个输入序列。该输入序列可以使用XPath表达式、原子值序列、分隔符序列或结构化函数。因此,这里的for语句与SQL SELECT FROM语句类似,但与编程语言中的“for”语句不同。
绑定变量同样可以使用for语句声明。
示例:使用for语句从简历中检索所有的家庭地址
下面的查询得出了JobCandidateID等于3的应聘者的address节点值等于Home的元素。
SELECT Resume.query('
declare namespace RES="http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/Resume";
for $A in /RES:Resume/RES:Address/RES:Addr.Type[.="Home"]/..
return
$A
') as Result
FROM [HumanResources].[JobCandidate]
WHERE JobCandidateID = 3
Where
FLWOR表达式中使用Where语句可以筛选一个迭代的结果。
示例:使用where语句选择所有的家庭地址
下面的查询得出了JobCandidateID等于3的应聘者的address节点值等于Home的元素。
SELECT Resume.query('
declare namespace RES="http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/Resume";
for $A in /RES:Resume/RES:Address
where $A/RES:Addr.Type[.="Home"]
return
$A
') as Result
FROM [HumanResources].[JobCandidate]
WHERE JobCandidateID = 3
order by
Order by关键字可以对返回结果集进行排序。Order by关键字可以使用一个排序表达式,这个表达式必须返回一个原子值。一般,你可以进行升序或者降序排列。默认的排序顺序是升序。
示例:使用order by语句对查询出来的历史职业进行升序排序
下面的查询对JobCandidateID等于3的应聘者所从事过的职业根据其工作时间进行了升序排序。
SELECT Resume.query('
declare namespace RES="http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/Resume";
for $EMP in /RES:Resume/RES:Employment
order by $EMP/RES:Emp.StartDate
return
$EMP
') as Result
FROM [HumanResources].[JobCandidate]
WHERE JobCandidateID = 3
return
Return语句,和SQL中的SELECT语句类似,能够指定查询的结果。你可以在return语句中使用任何一个有效的XQuery表达式。你同样也可以通过在return部分中说明元素的构造、属性等来构造XML结构。
示例:使用return语句选择历史职业
下面的查询得出了JobCandidateID等于3的应聘者所从事过的所有职业的开始日期,结束日期,工作单位名称,工作职位。
SELECT Resume.query('
declare namespace RES="http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/Resume";
for $EMP in /RES:Resume/RES:Employment
order by $EMP/RES:Emp.StartDate
return
<Employment>
{ $EMP/RES:Emp.StartDate }
{ $EMP/RES:Emp.EndDate }
{ $EMP/RES:Emp.OrgName }
{ $EMP/RES:Emp.JobTitle }
</Employment>
') as Result
FROM [HumanResources].[JobCandidate]
WHERE JobCandidateID = 3
目前在SQL Server 2005中,XQuery还不支持let语句。这方面更多内容可参见本文章节“不支持的特性和工作中的解决方案”。
FLWOR表达式vs. XPath表达式
当计划进行的查询在for变量和for语句体之间包含一个JOIN操作时,序列可以用XPath表达式表达,但会导致执行效率低,这个时候应该使用FLWOR语句来定义该序列。只有当下面列举条件中的一个或者多个得到满足时,才适合使用FLWOR表达式。
• 如果想对一个作为某表达式返回结果的序列值进行迭代,你可以通过在for语句中绑定一个继承结果集值的变量来实现。实例都是由在for语句范围内并且保持复制的新元素构成。
• 当你要对for语句的结果序列进行过滤,而过滤是基于一个谓词且该谓词还无法使用简单的XPath表达式进行说明时,使用where语句就能够实现对结果集的过滤。示例如下:
DECLARE @result xml
SET @result = '<Result />'
SELECT @Result.query(\'
for $i in (1, 2, 3), $j in (3, 4, 5)
where $i < $j
return sum($i + $j)
') as Result
• 如果你想基于一个简短表达式对结果集进行排序,那么可以使用order by语句实现。
• 当你要使用for语句的返回结果来定义返回结果集的类型时,使用return语句可以实现。
在所有其他案例中,推荐使用XPath表达式。
XQuery的操作
作为一种功能性语言,在SQL Server2005中支持XQuery的多种数据类型的函数和操作,可分类如下:
• 数学运算
• 比较运算
• 逻辑运算
表 3 SQL Server2005支持的运算符
数学运算符
SQL Server 2005支持五个数学运算符,分别是+、b、*、div和 mod。目前,还不支持idiv。
示例:转换表store中选定的信息的值
本示例中,假定数据库AdventureWorks中有表[Sales].[Store],下面的查询以日圆形式返回CustomerID等于3的仓库的年销售额和年收入,以平方米形式返回仓库的存储面积。
SELECT Demographics.query('
declare namespace ST="http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/StoreSurvey";
for $S in /ST:StoreSurvey
return
<StoreDetails
SalesInYen ="{ $S/ST:AnnualSales*106.8100 }"
RevenueInYen = "{ $S/ST:AnnualRevenue*106.8100 }"
StoreAreaInSqMeters = "{ $S/ST:SquareFeet*0.0929 }">
</StoreDetails>
') as Result
FROM [Sales].[Store]
WHERE CustomerID = 3
比较运算符
SQL Server 2005支持四种比较操作符—普通比较运算符、数值比较运算符、节点比较运算符和节点顺序比较运算符。
普通比较运算符
普通比较运算符可以进行原子值、序列或者两者的比较。普通比较运算符是=、 !=、<、>、<=和>=。普通比较必须是可以量化的,意思就是说任何比较的结果都是现实存在的。
示例:选择address类型节点值不等于Home的元素
下面的查询得出了JobCandidateID等于3的应聘者的所有address类型节点值不等于Home的元素:
SELECT Resume.query('
declare namespace RES="http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/Resume";
for $A in /RES:Resume/RES:Address
where $A/RES:Addr.Type[.!="Home"]
return
$A
') as Result
FROM [HumanResources].[JobCandidate]
WHERE JobCandidateID = 3
数值比较运算符
数值比较运算符可以进行原子值的比较。SQL Server 2005种支持的数值运算符是eq、ne、lt、gt、le和ge。目前XQuery还未能与其2004年7月份的设计书所设计的一样实现对非类型化的原子值的比较。在XQuery说明书中,非类型化原子值是被定义为能替代字符串类型的新类型。这么设计是因为我们考虑到,实现普通比较运算符和数值比较运算符结合,要比仅仅能够比较数值要重要的多。
示例:选择GPA超过3.5的教育背景
下面的查询得出了JobCandidateID等于2的应聘者的所有GPA超过3.5的教育背景。
SELECT Resume.query('
declare namespace RES="http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/Resume";
for $ED in /RES:Resume/RES:Education
where xs:decimal($ED/RES:Edu.GPA) gt 3.5
return
$ED
') as Result
FROM [HumanResources].[JobCandidate]
WHERE JobCandidateID = 2
节点比较运算符
你可以使用节点比较运算符来确定2个节点是否代表同一类节点。节点比较运算符是二元操作符,每个操作元都是node类型。
示例:比较确定两个address节点的类型
下面查询比较了2个address节点,确定它们在XML文档中是否代表同一类节点。
SELECT Resume.query('
declare namespace RES="http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/Resume";
if ( (/RES:Resume/RES:Address)[1] is (//RES:Address)[1] )
then
<Result>Nodes are equal</Result>
else
<Result>Nodes are not equal</Result>
') as Result
FROM [HumanResources].[JobCandidate]
WHERE JobCandidateID = 3
引用:
节点顺序比较运算符
你可以使用节点顺序比较运算符来确定一个XML文档中两个节点的顺序。SQL Server 2005中支持的节点顺序比较运算符是>>和<<,两者都是二元操作符。>>操作符当左边操作元在XML文档中位于右边操作元的前面时,返回true;<<操作符当左边操作元在文档中位于右边操作元的后面时,返回true。
示例:比较两个address节点的顺序
下面的查询比较了JobCandidateID等于3的应聘者的两个address节点的顺序。
SELECT Resume.query('
declare namespace RES="http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/Resume";
if ( (/RES:Resume/RES:Address/RES:Addr.Type[.="Home"])[1] << (/RES:Resume/RES:Address/RES:Addr.Type[.="Permanent"])[1] )
then
<Result>Home address precedes Permanent address</Result>
else
<Result>Home address follows Permanent address</Result>
') as Result
FROM [HumanResources].[JobCandidate]
WHERE JobCandidateID = 3
逻辑操作符
SQL Server 2005中支持的XQuery逻辑操作符是and和or。使用任何由这两个操作符组成的逻辑表达式的返回值都是true或者false。
示例:使用and操作符来创建一个逻辑表达式
下面的query返回了所有已经获得工商专业学士学位的应聘者。
SELECT Resume.query('
declare namespace RES="http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/Resume";
/RES:Resume/RES:Education[RES:Edu.Level="Bachelor" and RES:Edu.Major="Business"]
') as Result
FROM [HumanResources].[JobCandidate]
WHERE JobCandidateID = 3
if-then-else语句
和其他函数语言一样,XQuery支持if-then-else语句。你可以使用if-then-else语句来实现基于条件表达式返回值的操作。
示例:使用一个条件表达式
下面的查询得出了JobCandidateID等于3的应聘者简历中所有的address节点元素。
SELECT Resume.query('
declare namespace RES="http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/Resume";
for $A in /RES:Resume/RES:Address
return
if ( $A/RES:Addr.Type eq "Home" )
then
<Result>Home Address</Result>
else
<Result>Other Address</Result>
') as Result
FROM [HumanResources].[JobCandidate]
WHERE JobCandidateID = 3
引用:
使用XQuery构造XML
XQuery构造器能够在查询中创建XML结构。可以调用构造器的参数包括元素、属性、处理指令、文本节点和注释。下面举例说明了如何创建XML。
示例:使用常量表达式
下面的查询使用常量表达式构造了某应聘者的一个历史职业的XML结构。
SELECT Resume.query('
<Employer IndustryCategory="ITServices">
<Organization>ABC Technologies</Organization>
<JobTitle>Software Engineer</JobTitle>
<StartDate>2001-10-01</StartDate>
<EndDate>2003-05-09</EndDate>
</Employer>
') as Result
FROM [HumanResources].[JobCandidate]
WHERE JobCandidateID = 3
示例:使用动态数据
下面的查询通过查询JobCandidateID等于3的应聘者的数据,构造了一个历史职业的XML结构。
SELECT Resume.query('
declare namespace RES="http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/Resume";
for $EMP in /RES:Resume/RES:Employment
return
<Employer Organization = "{ $EMP/RES:Emp.OrgName }" >
{ $EMP/RES:Emp.StartDate }
{ $EMP/RES:Emp.EndDate }
{ $EMP/RES:Emp.JobTitle }
</Employer>
') as Result
FROM [HumanResources].[JobCandidate]
WHERE JobCandidateID = 3
示例:使用常量给计算元素和属性构造器命名
下面的查询得出了JobCandidateID等于3的应聘者的从业经历。
SELECT Resume.query('
declare namespace RES="http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/Resume";
for $EMP in /RES:Resume/RES:Employment
return
element Employer
{
attribute Organization { $EMP/RES:Emp.OrgName },
element StartDate { string($EMP/RES:Emp.StartDate) },
element EndDate { string($EMP/RES:Emp.EndDate) },
element JobTitle { string($EMP/RES:Emp.JobTitle) }
}
') as Result
FROM [HumanResources].[JobCandidate]
WHERE JobCandidateID = 3
引用:
XQuery构造器 vs. FOR XML语句
某些应用需要根据行集来构造XML。在服务器端,XML的构造可以通过FOR XML语句或者XQuery构造器或者操作XML DML。推荐使用FOR XML和XQuery构造器来构造XML的原因如下:
• 如果XML的数据来自多个列和行,那么FOR XML是唯一的选择。
• 如果单个XML实例需要重新构造,那么可以使用XQuery和FOR XML。但XQuery可能更有效,因为FOR XML需要XML实例中更多的XML数据类型信息。
• 你可以使用多个XML DML语句来构造一个XML实例。但这个方法的执行效率明显低于XQuery构造器。
• 在SQL Server 2005中,FOR XML语句可以使用新类型把查询的结果直接构造成一个XML数据类型的实例。
XQuery内置函数
SQL Server 2005中的XQuery实现了对XQuery 1.0和XPath 2.0内置函数子集的支持。这些函数包括数据访问函数、字符串处理函数、聚集函数、上下文函数、算数函数、布尔值函数、节点函数和序列函数。下面将分析对其中的部分函数。
数据访问
你可以使用数据访问函数来提取字符串或者类型化节点的值。XQuery支持两种类型的数据访问函数:string()提取节点中的字符串值;data()提取类型化节点值。如果节点不是文本节点,不是属性节点,也不是元素节点,那么data()函数抛出一个状态错误。如果节点是一个非类型化XML实例的文档节点,那么data()返回文档的字符串值。如果节点是一个复合类型元素,data()返回一个状态错误。
示例:使用string()函数
参见章节“使用XQuery构造XML”中的示例“使用常量给计算元素和属性构造器命名”,示例是一个查询,通过使用string()函数和计算元素构造器来构建应聘者的职业经历的XML。
示例:使用data()函数
下面的查询通过使用data()函数和计算元素构造器来构建应聘者的职业经历的XML。
SELECT Resume.query('
declare namespace RES="http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/Resume";
for $ED in /RES:Resume/RES:Education
where xs:decimal( data($ED/RES:Edu.GPA) ) gt 3.5
return
element Education
{
element Level { data($ED/RES:Edu.Level) },
element Degree { data($ED/RES:Edu.Degree) },
element GPA { data($ED/RES:Edu.GPA) },
element GPAScale { data($ED/RES:Edu.GPAScale) }
}
') as Result
FROM [HumanResources].[JobCandidate]
WHERE JobCandidateID = 2
字符串的处理
XQuery支持四个字符串处理函数:
• concat()连接两个或者多个字符串。
• contains() 确定第一个操作元中是否包含和第二个操作元一样的字符串。搜索字符串的长度要小于4000(Unicode码)。
• substring() 提取输入字符串在另外一个字符串中的位置。
• string-length() 计算字符串的长度.
SQL Server 2005目前的版本只支持Unicode codepoint的整理。
示例:使用concat() 和substring()函数
下面的查询通过连接开始日期,结束日期,工作单位名称和职位来构建应聘者职业经历的XML:
SELECT Resume.query('
declare namespace RES="http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/Resume";
for $EMP in /RES:Resume/RES:Employment
return
<Employment>
{
concat( substring(string($EMP/RES:Emp.StartDate),1,10)," to ",
substring(string($EMP/RES:Emp.EndDate),1,10), ", ",
string($EMP/RES:Emp.OrgName), ", ",
string($EMP/RES:Emp.JobTitle) )
}
</Employment>
') as Result
FROM [HumanResources].[JobCandidate]
WHERE JobCandidateID = 3
示例:使用contains()函数
下面的查询举例说明了contains()函数的用法,查询显示Edu.Degree节点元素值中包含字符串science的应聘者的教育信息。
SELECT Resume.query('
declare namespace RES="http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/Resume";
for $ED in /RES:Resume/RES:Education
where contains($ED/RES:Edu.Degree, "Science")
return
element Education
{
element Level { data($ED/RES:Edu.Level) },
element Degree { data($ED/RES:Edu.Degree) },
element GPA { data($ED/RES:Edu.GPA) },
element GPAScale { data($ED/RES:Edu.GPAScale) }
}
') as Result
FROM [HumanResources].[JobCandidate]
WHERE JobCandidateID = 2
引用:
聚集函数
聚集函数输入一个节点序列,然后返回序列的聚集值。在SQL Server 2005中,XQuery目前支持的聚集函数是count()、min()、max()、avg()和sum()。其中,只有支持gt操作的类型(i.e.、三个内置算数基本类型、日期类型、xs:string、xs:boolean和xdt: untypedAtomic)才能使用min()和max()函数。三个函数不支持混合类型的序列。更进一步,xdt:untypedAtomic被认为是xs:double类型。
然后,输入的表达式必须是三个内置算数函数类型或者untypedAtomic(但不能是混合型,xdt:untypedAtomic被认为是xs:double类型)的子类型,才能使用avg()和sum()函数。
最后,count()函数返回序列中节点的数量。
示例:使用count()函数
下面的查询使用count()函数得出了文档中employment、education和address节点的数量。
SELECT Resume.query('
declare namespace RES="http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/Resume";
<Employment>Element count is { count(/RES:Resume/RES:Address) }</Employment>,
<Education>Element count is { count(/RES:Resume/RES:Education) }</Education>,
<Address>Element count is { count(/RES:Resume/RES:Address) }</Address>
') as Result
FROM [HumanResources].[JobCandidate]
WHERE JobCandidateID = 3
示例:使用min()函数
下面的查询使用min()函数得出了GPA值最小的education节点。
SELECT Resume.query('
declare namespace RES="http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/Resume";
for $ED in /RES:Resume/RES:Education
where $ED/RES:Edu.GPA = min(/RES:Resume/RES:Education/RES:Edu.GPA)
return
$ED
') as Result
FROM [HumanResources].[JobCandidate]
WHERE JobCandidateID = 2
示例:使用max()函数
下面的查询使用max()函数得出了GPA值最大的education节点。
SELECT Resume.query('
declare namespace RES="http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/Resume";
for $ED in /RES:Resume/RES:Education
where $ED/RES:Edu.GPA = max(/RES:Resume/RES:Education/RES:Edu.GPA)
return
$ED
') as Result
FROM [HumanResources].[JobCandidate]
WHERE JobCandidateID = 2
示例:使用avg()函数
下面的查询使用avg()函数计算了纽约和波士顿的每周平均最高和最低的温度。
DECLARE @Weather xml
SET @Weather = '
<WeatherInfo>
<NewYork>
<Temp Date="2004-11-01" High="55" Low="45" />
<Temp Date="2004-11-02" High="58" Low="42" />
<Temp Date="2004-11-03" High="60" Low="40" />
<Temp Date="2004-11-04" High="51" Low="47" />
<Temp Date="2004-11-05" High="54" Low="41" />
<Temp Date="2004-11-06" High="55" Low="43" />
<Temp Date="2004-11-07" High="58" Low="47" />
</NewYork>
<Boston>
<Temp Date="2004-11-01" High="53" Low="45" />
<Temp Date="2004-11-02" High="56" Low="42" />
<Temp Date="2004-11-03" High="54" Low="41" />
<Temp Date="2004-11-04" High="52" Low="45" />
<Temp Date="2004-11-05" High="52" Low="36" />
<Temp Date="2004-11-06" High="54" Low="41" />
<Temp Date="2004-11-07" High="56" Low="44" />
</Boston>
</WeatherInfo>'
SELECT @Weather.query(\'
<WeatherInfo>
<NewYork>
<AvgHigh>{ avg(/WeatherInfo/NewYork/Temp/@High) }</AvgHigh>
<AvgLow>{ avg(/WeatherInfo/NewYork/Temp/@Low) }</AvgLow>
</NewYork>
<Boston>
<AvgHigh>{ avg(/WeatherInfo/Boston/Temp/@High) }</AvgHigh>
<AvgLow>{ avg(/WeatherInfo/Boston/Temp/@Low) }</AvgLow>
</Boston>
</WeatherInfo>
') as Result
示例:使用sum()函数
下面的查询使用sum()和count()函数计算了纽约和波士顿的每周平均最高和最低的温度。
DECLARE @Weather xml
SET @Weather = '
<WeatherInfo>
<NewYork>
<Temp Date="2004-11-01" High="55" Low="45" />
<Temp Date="2004-11-02" High="58" Low="42" />
<Temp Date="2004-11-03" High="60" Low="40" />
<Temp Date="2004-11-04" High="51" Low="47" />
<Temp Date="2004-11-05" High="54" Low="41" />
<Temp Date="2004-11-06" High="55" Low="43" />
<Temp Date="2004-11-07" High="58" Low="47" />
</NewYork>
<Boston>
<Temp Date="2004-11-01" High="53" Low="45" />
<Temp Date="2004-11-02" High="56" Low="42" />
<Temp Date="2004-11-03" High="54" Low="41" />
<Temp Date="2004-11-04" High="52" Low="45" />
<Temp Date="2004-11-05" High="52" Low="36" />
<Temp Date="2004-11-06" High="54" Low="41" />
<Temp Date="2004-11-07" High="56" Low="44" />
</Boston>
</WeatherInfo>'
SELECT @Weather.query(\'
<WeatherInfo>
<NewYork>
<AvgHigh>{ sum(/WeatherInfo/NewYork/Temp/@High) div count(/WeatherInfo/NewYork/Temp/@High) }</AvgHigh>
<AvgLow>{ sum(/WeatherInfo/NewYork/Temp/@Low) div count(/WeatherInfo/NewYork/Temp/@Low) }</AvgLow>
</NewYork>
<Boston>
<AvgHigh>{ sum(/WeatherInfo/Boston/Temp/@High) div count(/WeatherInfo/Boston/Temp/@High) }</AvgHigh>
<AvgLow>{ sum(/WeatherInfo/Boston/Temp/@Low) div count(/WeatherInfo/Boston/Temp/@Low) }</AvgLow>
</Boston>
</WeatherInfo>
') as Result
上下文函数
你可以使用上下文函数来获得某上下文节点的上下文关系属性。SQL Server 2005中实现了两个上下文函数—last() 和 position()。last()函数可以确定一个序列中节点的数量,而position()函数可以获得一个上下文节点的位置。毫无疑问,在SQL Server 2005中,last() 和 position()函数只能被各节点间关系独立的上下文使用。
示例:使用last()函数
下面的查询使用last()函数获得某应聘者的最后一个address节点。
SELECT Resume.query('
declare namespace RES="http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/Resume";
/RES:Resume/RES:Address[last()]
') as Result
FROM [HumanResources].[JobCandidate]
WHERE JobCandidateID = 3
示例:使用position()函数
下面的查询使用position()函数获得某应聘者的最后一个address节点。
SELECT Resume.query('
declare namespace RES="http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/Resume";
/RES:Resume/RES:Address[position()<=2]
') as Result
FROM [HumanResources].[JobCandidate]
WHERE JobCandidateID = 3
关系类型表达式
XQuery根据类型的信息可以支持不同类型的表达式和操作。这些表达式可以分类为类型声明表达式、类型检测表达式和类型构造表达式。这些表达式将是下部分主要探讨的内容。
类型声明表达式
如for语句中的xs:TYPE
你可以使用as语句来说明for语句中使用的捆绑变量的类型。
当绑定变量实际的值不是变量本身的类型时,会返回一个类型错误。
示例:使用“as xs:TYPE”语句
下面的查询将address节点序列绑定在变量$A,变量定义为类型元素(RES:Address):
SELECT Resume.query('
declare namespace RES="http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/Resume";
for $A as element(RES:Address) in /RES:Resume/RES:Address
return
$A
') as Result
FROM [HumanResources].[JobCandidate]
WHERE JobCandidateID = 2
类型检查表达式
xs:TYPE实例
使用instance of可以识别XML文档中单个节点的类型。
示例:使用"instance of xs:TYPE"
下面的查询检查确定一个用xPath表达式标识的address节点的类型是否是element()类型:
SELECT Resume.query('
declare namespace RES="http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/Resume";
if ( (/RES:Resume/RES:Address)[1] instance of element() )
then
<Result>Selected node is an Element</Result>
else
<Result>Selected node is not an Element</Result>
') as Result
FROM [HumanResources].[JobCandidate]
WHERE JobCandidateID = 3
类型转换函数
隐式类型转换
对于包含算数操作符或者功能符合的表达式,XQuery引擎实现了对表达式中数字类型和非类型化原子值的隐式类型转换。当一个表达式返回数字类型不是期望的类型时,就需要进行类型转换。而类型转换就是把表达式的返回类型转换为需要的类型。
示例:隐式类型转换
下面的查询完成了对小数和double值得操作。在本示例中,表达式只有先把xs:decimal转换成xs:double 才能进行相加。
DECLARE @result xml
SET @result = '<Result />'
SELECT @Result.query(\'
<Result>{ xs:decimal("10.55") + xs:double(1.5e1) }</Result>
') as Result
显式类型转换
值类型构造器
在XML Schema 规范中对所有的内置类型定义了构造函数。这些构造函数不仅可以构造类型化的值还可以进行类型转换。XQuery还可以用于为输入的schemas定义类型。
示例:使用值类型构造器实现值的构造
下面的查询得出了所有StartDate大于xs:date类型构造的值的Employment节点:
SELECT Resume.query('
declare namespace RES="http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/Resume";
for $EMP in /RES:Resume/RES:Employment
where $EMP/RES:Emp.StartDate gt xs:date("1995-01-01")
return
$EMP
') as Result
FROM [HumanResources].[JobCandidate]
WHERE JobCandidateID = 3
示例:使用值类型构造器实现类型转换
下面的查询得出了JobCandidateID等于2的应聘者的所有GPA大于或者等于3.8的Education节点。本示例使用xs:decimal类型的值构造器实现了把Edu.GPA的值从xs:string类型转换为xs:decimal类型:
SELECT Resume.query('
declare namespace RES="http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/Resume";
for $ED in /RES:Resume/RES:Education
where xs:decimal( data($ED/RES:Edu.GPA) ) ge 3.8
return
element Education
{
element Level { string($ED/RES:Edu.Level)},
element StartDate { string($ED/RES:Edu.StartDate)},
element EndDate { string($ED/RES:Edu.EndDate)},
element Degree { string($ED/RES:Edu.Degree)},
element GPA { string($ED/RES:Edu.GPA)},
element GPAScale { string($ED/RES:Edu.GPAScale)}
}
') as Result
FROM [HumanResources].[JobCandidate]
WHERE JobCandidateID = 2
引用:
cast as xs:TYPE ?操作符
在SQL Server 2005中,XQuery支持cast as TYPE ?操作符,能够完成显式类型转换。使用xs:TYPE()构造器也可以完成显式类型转换,而且写起来也比cast as TYPE ?操作符更简洁。
示例:使用“cast as TYPE ?”操作符
下面的查询为JobCandidateID等于3的应聘者创建了一个XML文档,其数据选自该应聘者的Education节点:
SELECT Resume.query('
declare namespace RES="http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/Resume";
for $ED in /RES:Resume/RES:Education
return
element Education
{
element Level { $ED/RES:Edu.Level cast as xs:string? },
element StartDate { $ED/RES:Edu.StartDate cast as xs:date? },
element EndDate { $ED/RES:Edu.EndDate cast as xs:date? },
element Degree { $ED/RES:Edu.Degree cast as xs:string? },
element GPA { $ED/RES:Edu.GPA cast as xs:decimal? },
element GPAScale { $ED/RES:Edu.GPAScale cast as xs:decimal? }
}
') as Result
FROM [HumanResources].[JobCandidate]
WHERE JobCandidateID = 2
示例:使用“xs:TYPE()”操作符
下面的查询内容和结果都同前一个示例,但用xs:TYPE()操作符替代了cast as xs:TYPE ?操作符:
SELECT Resume.query('
declare namespace RES="http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/Resume";
for $ED in /RES:Resume/RES:Education
return
element Education
{
element Level { xs:string($ED/RES:Edu.Level) },
element StartDate { xs:date($ED/RES:Edu.StartDate) },
element EndDate { xs:date($ED/RES:Edu.EndDate) },
element Degree { xs:string($ED/RES:Edu.Degree) },
element GPA { xs:decimal($ED/RES:Edu.GPA) },
element GPAScale { xs:decimal($ED/RES:Edu.GPAScale) }
}
') as Result
FROM [HumanResources].[JobCandidate]
WHERE JobCandidateID = 2
引用:
访问关系性的列和变量
当使用XQuery来写查询语句的时候,极少情况下会遇到在查询中需要访问关系性的列和变量。但SQL Server 2005为了满足这种需求,提供了两个函数—sql:column()和 sql:variable()。函数sql:column()可以在查询中访问关系表中非XML的列。在特定情况下可以使用该函数,比如将XML和多个表中非XML的列的信息进行聚集,使用非XML列的值来过滤某XQuery的返回值等等。datetime、CLR用户自定义函数或者XML不能调用函数sql:column()和 sql:variable()。
示例:使用sql:column()函数
下面的查询创建了一个XML文档,数据来自非XML类型的列Name和CustomerID,还来自XML类型的列Demographics中的元素YearOpened、NumberOfEmployees、AnnualSales和AnnualRevenue。
SELECT Demographics.query('
declare namespace ST="http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/StoreSurvey";
element CustomerInfo
{
element CustomerID { sql:column("Store.CustomerID") },
element Name { sql:column("Store.Name") },
element YearOpened { string((/ST:StoreSurvey/ST:YearOpened)[1]) },
element NumberOfEmployees { string((/ST:StoreSurvey/ST:NumberEmployees)[1]) },
element AnnualSales { string((/ST:StoreSurvey/ST:AnnualSales)[1]) },
element AnnualRevenue { string((/ST:StoreSurvey/ST:AnnualRevenue)[1]) }
}
') as Result
FROM [Sales].[Store] Store
WHERE Store.CustomerID = 4
示例:使用sql:variable()函数
在下面的存储过程中,通过在XQuery的where语句中使用变量@AddrType的值对数据进行过滤,返回了满足过滤条件的应聘者的home address节点:
CREATE PROCEDURE [GetCandidateAddress]
@JobCandidateID [int],
@AddrType [varchar](20)
AS
BEGIN
SET NOCOUNT ON;
SELECT Resume.query('
declare namespace RES="http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/Resume";
for $A in /RES:Resume/RES:Address
where $A[ RES:Addr.Type = sql:variable("@AddrType") ]
return
$A
') as Result
FROM [HumanResources].[JobCandidate]
WHERE JobCandidateID = @JobCandidateID
END
示例:结合使用XML数据类型的exist()方法和sql:variable()函数
下面的查询返回了所有获得商业专业学士学位的应聘者的简历:
DECLARE @EducationLevel varchar(20)
SET @EducationLevel = 'Bachelor'
DECLARE @major varchar(20)
SET @major = 'Business'
SELECT JobCandidateID, Resume
FROM [HumanResources].[JobCandidate]
WHERE Resume.exist ('
declare namespace RES="http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/Resume";
/RES:Resume/RES:Education[ RES:Edu.Level = sql:variable("@EducationLevel") and RES:Edu.Major = sql:variable("@major") ]') = 1
不支持的特性和工作中的解决方案
目前SQL Server 2005中,XQuery不支持的特性如下:
• Let 语句:作为FLWOR表达式一部分的let语句,能为表达式的返回值绑定一个变量。解决方案-可以用inline表达式代替let语句使用。
• Range表达式(to操作符):Range表达式能够使用to操作符来构架自增的整数序列。比如,一个range表达式(6to10),它可以构架队列(6, 7, 8, 9, 10)。解决方案—你可以列出序列的所有值,以代替使用to操作符。
• 类型信息:目前还不支持基于类型系统的某些特性,如表达式typeswitch、treat as、 castable和validate。
• Typeswitch表达式类似与其它程序语言中的switch-case结构。在switch-case语句中,每个分支都是基于switch输入的条件的值;而在typeswitch表达式中,每个分支都是基于typeswitch输入的条件的类型。解决方案—使用if then else和instance of语句来代替。
• Treat as表达式能够将表达式返回的静态类型转换为指定的静态类型,并且当表达式的返回静态类型与指定静态类型不匹配时,会报一个静态类型错误。它不能转换表达式返回的动态类型或者值。解决方案—无。
• Castable表达式能够检查确认一个原子值是否可以转换为指定的类型。解决方案—使用表达式“empty(data($x)) or not(empty(T($x)))” 代替“$x castable as T?”。
• Validate表达式在目前范畴内,是把基于schema定义的条件有效化。解决方案—使用Transact-SQL转换schema collection,来替代使用validate表达式。
• 可重用性(Reusability):与其它程序语言一样,它可以编写可重用性的函数,和已知的用户自定义函数一样,包含可以调用和整合的查询。另外,它还可以把用户自定义的函数如模块的聚集进行打包。查询能够通过引入模块,调用模块中的函数。模块能够在查询的前缀部分进行引入。SQL Server 2005对这个特性没有解决方案。
• Built-in functions:目前在SQL Server 2005中,还不支持下面列举的内置函数。关于这些函数的更多内容,参见网站W3C.org的《XQuery 1.0和XPath 2.0的函数与操作符》(XQuery 1.0 and XPath 2.0 Functions and Operators)。
• 访问符(accessors):fn:node-name(), fn:nilled(), fn:base-uri(), fn:document-uri()。
• 错误函数(error function): fn:error().
• 跟踪函数(trace function): fn:trace().
• 数值函数(functions on numeric values): abs(), round-half-to-even()。
• 字符串处理函数(string-handling functions): codepoints-to-string(), string-to-codepoints(), compare(), string-join(), normalize-space(), normalize-unicode(), upper-case(), lower-case(), translate(), escape-uri(), starts-with(), ends-with(), substring-before(), substring-after(), matches(), replace(), tokenize()。
• 统一资源标识的函数和操作符(functions and operators for anyURI): resolve-uri()。
• 时间段、日期、时间的函数和操作符(functions and operators on durations, dates, and time): years-from-duration(), months-from-duration(), days-from-duration(), hours-from-duration(), minutes-from-duration(), seconds-from-duration(), year-from-dateTime(), month-from-dateTime(), month-from-dateTime(), day-from-dateTime(), hours-from-dateTime(), minutes-from-dateTime(), seconds-from-dateTime(), timezone-from-dateTime(), year-from-date(), month-from-date(), day-from-date(), timezone-from-date(), hours-from-time(), minutes-from-time(), seconds-from-time(), timezone-from-time(), adjust-dateTime-to-timezone(), adjust-date-to-timezone(), adjust-time-to-timezone(), subtract-dateTimes-yielding-yearMonthDuration(), subtract-dateTimes-yielding-dayTimeDuration(), subtract-dates-yielding-yearMonthDuration(), subtract-dates-yielding-dayTimeDuration(). 类型xdt:dayTimeDuration 和 xdt:yearMonthDuration也不支持。
• QNames相关函数(functions related to QNames): resolve-QName(), QName(), namespace-uri-for-prefix(), in-scope-prefixes()。
• 节点函数(functions on nodes): name(), lang(), root(). 解决方案–使用 of root()代替。
• 序列的函数和操作符(functions and operators on sequences): fn:boolean(), fn:index-of(), fn:exists(), insert-before(), remove(), reverse(), subsequence(), unordered(), zero-or-one(), one-or-more(), exactly-one(), deep-equal(),two-argument version of id(), idref(), doc(), collection()函数和集合, intersect和except 操作符。解决方案 – 使用not(not()) 代替fn:boolean(),使用 not(empty())代替fn:exists(). 使用重复说明代替zero-or-one(),使用[1]代替exactly-one()。
• 上下文函数(context functions): current-dateTime(), current-date(), current-time(), default-collation(), implicit-timezone()。
• 位置变量(positional variable):位置变量可以使用at语句定义为一个FLWOR语句中的某部分,它能够标识某表达式返回结果中的节点位置。
• 改变顺序(order):使用order by语句来改变“empty greatest | least”顺序是不支持的。
• 其它特征:下面的特征也不提供支持:
• Idiv操作符。
• 不支持从外部导入schema。
• 不支持外部变量。
• 不支持无边界的保存条件。
• 不支持不同类型序列的串联,如节点和数据。
• 不支持保存0时间。
• 不支持使用一个简单类型的内容来访问一个元素中的文本节点。
• 不支持在一个类型化XML数据类型应用中访问xsi:*属性。
最佳实践和指导方针
• 尽可能利用可以使用的类型的特点。它能够提高执行效率和通过静态类型来监测错误。
• 如果你想提取XML数据类型的部分属性并在关系性查询中使用它们,那么使用XQuery可以提高性能。通常,有用的属性都在关系性数据表中都作为索引保存,以提高性能。
• 使用递增定义,如[1]或者FLWOR来避免错误匹配基数而导致的静态错误。
• 使用外部转换来避免静态类型错误。
• 索引的使用:如果你要使用繁琐的XPath表达式来查询,就创建一个PATH索引。当XQuery查中的XPath表达式使用模糊路径(e.g., /a/* or //b)来查询元素或者属性时,就使用一个VALUE索引。当查询中包含对XML文档进行某已知属性的所有节点查询的时候,就使用PROPERTY索引。
• 当涉及的大部分类型都是同一个命名空间时,使用默认命名空间。或者,使用一个前缀。
最佳实践的更多内容,参见Microsoft Developer Network (MSDN)的《XML在微软SQL Server 2005上的最佳实践》(XML Best Practices for Microsoft SQL Server 2005)和《XML数据类型的最佳应用》(Performance Optimizations for the XML Data Type)
引用:
XML数据的修改
SQL Server 2005支持对存储在数据库中的XML数据进行修改。目前,W3C XQuery工作草图还没有为XML文档的修改定义一个规范。为了提供修改XML文档的支持,微软研发了XML数据修改语言(XML Data Modification Language DML)。XML文档可以使用XML数据类型的modify方法修改,并且可以使用XML DML语句指定修改。
XML DML分别使用insert、 delete和 replace value of关键字对XML文档进行插入、删除和更新操作。一个类型化XML文档的修改是以根据对XML数据类型定义的schema约束进行的有效检查为前提的。
下面的表和XML文档举例说明了XML DML的操作:
表:
CREATE TABLE [CandidateInfoXMLDataType]
(
[JobCandidateID] [int] IDENTITY(1,1) NOT NULL,
[Resume] [xml] NOT NULL,
[ModifiedDate] [datetime] NOT NULL DEFAULT (getdate())
)
XML实例:
<JobCandidate>
<Name>
<FirstName>Mike</FirstName>
<MiddleName></MiddleName>
<LastName>Chen</LastName>
</Name>
<Address>
<Address1>34 181st Place SE</Address1>
<Address2>Apt 3344</Address2>
<City>Redmond</City>
<State>WA</State>
<Country>US</Country>
<PhoneNumber>9870909023</PhoneNumber>
</Address>
<Education>
<BachelorDegree>BS</BachelorDegree>
<MasterDegree>MS</MasterDegree>
</Education>
<Skills>
<Skill>ASP.NET</Skill>
<Skill>SQL</Skill>
</Skills>
<Employement>
<Employer>
<OrgName>ABC Technologies</OrgName>
<Location>NY, US</Location>
<StartDate>20/02/2000</StartDate>
<EndDate>10/04/2004</EndDate>
<JobTitle>Project Leader</JobTitle>
<Responsibility>Responsible for design,development,testing activities</Responsibility>
</Employer>
</Employement>
</JobCandidate>
插入操作
你可以使用insert关键字在XML文档中插入多个节点。Insert关键字接受XQuery表达式标识的待插入的节点,并支持XQuery表达式来标识插入位置的参照节点。
另外,你还可以使用into, after和before关键字来标识相对于参照节点的新节点的位置。当你使用into关键字,新节点将作为参照节点的子节点被插入。如果你包含into关键字,你同时要使用as first或者as last关键字来标识插入节点的位置和参照节点的当前子节点的位置关系。你可以使用after和before关键字将新节点作为参照节点的同级节点插入参照节点后或者前的位置。
如果参照表达式(表达式2)不能静态地标识某个节点,那么插入操作失败,并返回一个静态错误。
语法:
insert
Expression1 (
{{{as first | as last} into} | after | before}
Expression2 )
示例:插入一个新技能
下面的存储过程允许用户插入特定应聘者的某个新技能。本存储过程使用sql:variable()函数来访问XML DML语句中的Transact-SQL变量。更多关于如何在XML中绑定非XML关系型数据的内容,参见《关系型列和变量的访问》(Accessing Relational Columns and Variables)。
下面的存储过程假定用户要把一个字符串类型的技能值作为第二个参数传递到存储过程中。本存储过程可以修改为接受一个包含多个技能元素的XML片段,这样就实现了一次调用存储过程就完成多个技能节点的插入。
/* Stored procedure to insert a new skill element for a candidate */
CREATE PROCEDURE [InsertSkillInfo]
@JobCandidateID [int],
@Skill [varchar](200)
AS
BEGIN
SET NOCOUNT ON;
UPDATE [CandidateInfoXMLDataType]
SET Resume.modify('
insert element Skill {sql:variable("@Skill")}
as last
into (/JobCandidate/Skills)[1]
')
WHERE JobCandidateID = @JobCandidateID
END
删除操作符
Delete关键字可以在XML文档中删除多个节点。Delete关键字接受在XML文档中删除使用XQuery表达式标识的多个节点。
语法
delete表达式
示例:删除一个技能
下面举例说明了使用delete关键字删除某特定应聘者的单个技能。
下面的存储过程假定用户要把一个字符串类型的技能值作为第二个参数传递到存储过程中。本存储过程可以修改为接受一个包含多个技能元素的XML片段,这样就实现了一次调用存储过程就完成多个技能节点的删除。
/* Stored procedure to delete a specified skill element for a candidate */
CREATE PROCEDURE [DeleteSkillInfo]
@JobCandidateID [int],
@Skill [varchar](200)
AS
BEGIN
SET NOCOUNT ON;
UPDATE [CandidateInfoXMLDataType]
SET Resume.modify('
delete (/JobCandidate/Skills/Skill[.=sql:variable("@Skill")])
')
WHERE JobCandidateID = @JobCandidateID
END
本存储过程可以简单的修改为接收包含多个技能元素的XML段落,这样用户就可以在一次调用存储过程中完成多个技能节点的删除。
引用:
Update操作符
Replace value of关键字可以修改现存节点的值。为了修改现在节点的值,你必须定义一个XQuery表达式来标识那些需要更新值的节点,还要定义一个表达式来标识节点的新值。
当修改一个非类型化XML文档时,新节点值的表达式必须返回唯一的一个类型化节点。对于一个类型化XML文档来说,新节点值的表达式的类型必须是原表达式的类型或者子类型。
语法:
replace value of
Expression1
with
Expression2
Example: Updating a skill
示例:更新一个技能
下面举例说明了如何使用replace value of关键字来更新某特定应聘者的一个现存技能值。
/* Stored procedure to update a specified skill element for a candidate */
CREATE PROCEDURE [UpdateSkillInfo]
@JobCandidateID [int],
@SkillOld [varchar](200),
@SkillNew [varchar](200)
AS
BEGIN
SET NOCOUNT ON;
UPDATE [CandidateInfoXMLDataType]
SET Resume.modify('
replace value of (/JobCandidate/Skills/Skill[.=sql:variable("@SkillOld")]/text())[1]
with sql:variable("@SkillNew")
')
WHERE JobCandidateID = @JobCandidateID
END
与delete操作符不同,update 和 insert操作符只能在每次操作中操作一个节点。
XQuery应用场景
场景1:性能评价系统
某工作单位的人力资源部门需要一个性能评价系统来处理典型的公司绩效考核。通常,评价记录包含的信息都是使用自然语言描述的,如职员在评估期间关键项上的表现,说明下一个评估期间的主要项内容,表明员工的培训需求等等。XML是最适合处理这类信息的。职员的评价信息被保存在类型化XML的列中,使用XQuery可以让用户在系统中查询并分析职员的历史表现。更进一步说,XML DML可以修改评价记录。
现在,让我们假定工作单位的培训部门正在进行一个ASP.NET培训,并希望邀请所有需要培训的员工加入ASP.NET的培训课堂。下面的查询选择了所有在评价过程中确定需要接受ASP.NET培训的职员。
SELECT Appraisal.query('
for $PA in /PerformanceAppraisal,
$Skill in $PA/TrainingNeeds/Technical/Skill
where contains($Skill, "ASP.NET")
return
element Employee
{
element EmpID { data($PA/Employee/EmployeeID) },
element EmpName { data($PA/Employee/EmployeeName) },
element EMail { data($PA/Employee/EMailID) },
element Skill { data($Skill) }
}
') as Result
FROM [EmployeePerformanceAppraisal]
WHERE Appraisal.exist('/PerformanceAppraisal/TrainingNeeds/Technical/Skill/text()[contains(.,"ASP.NET")]') = 1
场景2:病例系统
某医院需要一个能获取病人病情的系统。病例包括病人个人信息,保险信息,病情描述,医生诊断,治疗情况等等。可以根据不同需要对病例进行查询分析。XML格式上满足是存储病例内容,如症状,检验报告和包含描述信息的治疗过程。病例可以存储到XML类型的列中。医院可以使用XQuery来分析这些信息。
下面的表和XML文档举例说明了在病例场景中XQuery的应用。
表:
CREATE TABLE [MedicalRecords](
[PatientID] [int] IDENTITY(1,1) NOT NULL,
[PatientRecord] [xml] NOT NULL,
[ModifiedDate] [datetime] NOT NULL DEFAULT (getdate())
,
PRIMARY KEY CLUSTERED
(
[PatientID] ASC
) ON [PRIMARY]
) ON [PRIMARY]
GO
CREATE PRIMARY XML INDEX idx_PatientRecord on [MedicalRecords] (PatientRecord)
GO
CREATE XML INDEX idx_PatientRecord_Path on [MedicalRecords] (PatientRecord) USING XML INDEX idx_PatientRecord FOR PATH
XML实例:
<PatientRecord>
<PatientDetails>
<Name>Robert</Name>
<Gender>Male</Gender>
<Age>5</Age>
<InsuranceInfo>
<Company>Blue Cross Blue Shield</Company>
<ID>D8456798</ID>
</InsuranceInfo>
</PatientDetails>
<HospitalDetails>
<Name>KK Hospital</Name>
<Department>Pediatrics</Department>
</HospitalDetails>
<AdmissionDetails>
<RegistrationNo>D4321</RegistrationNo>
<DateAdmitted>2004-05-02</DateAdmitted>
<DateDischarged>2004-05-08</DateDischarged>
</AdmissionDetails>
<ProblemDetails>
<Symptoms>
<Symptom>Abdominal pain</Symptom>
<Symptom>Dehydration</Symptom>
</Symptoms>
<Diagnosis>Diarrhea</Diagnosis>
<TreatmentInfo>
<Therapy>Oral Rehydration Therapy</Therapy>
<PrescriptionDetails>
<Item>Pepto-Bismol</Item>
<Item>Electrolyte Solutions</Item>
</PrescriptionDetails>
</TreatmentInfo>
</ProblemDetails>
</PatientRecord>
现在,让我们假定某医生对查看有“发烧”和“腹痛”症状的病例感兴趣。下面的查询得到了全部有“发烧”和“腹痛”症状的病例。
SELECT PatientID, PatientRecord.query('
element PatientName { data(/PatientRecord/PatientDetails/Name) },
element MedicalInfo { /PatientRecord/ProblemDetails }
') as Result
FROM [MedicalRecords]
WHERE PatientRecord.exist('/PatientRecord/ProblemDetails/Symptoms/Symptom/text()[contains(.,"Fever")]') = 1
AND PatientRecord.exist('/PatientRecord/ProblemDetails/Symptoms/Symptom/text()[contains(.,"Abdominal Pain")]') = 1
场景3:资产管理系统
某工作单位的信息技术部门需要开发一个应用来管理硬件设备和软件程序资产。这个应用必需跟踪的硬件的信息包括设备编号,用户信息,处理器/内存/BIOS/主板/声卡等系统信息,系统自带的软件,买卖信息和版本信息;应用必需维护的公司的软件信息包括软件类型,买入的授权号等等。应用还必需扩展支持未来可能出现的新资产类型。资产管理系统还可以用来生成报表,如硬件使用情况,软件授权号使用情况和硬件设备的维护费用。在这个场景中,需用在一个表中的同列中保存由不同schema说明的信息。多个schema的非类型化XML可以用来保存信息。一个schema collection的类型化XML也可以保存信息。存储成XML数据类型的资产信息可以使用XQuery来查询。
现在,让我们假定信息技术部门计划选择预装了微软Windows® XP 和微软 Office 2000软件的计算机系统。下面的查询选择了预装了Windows XP 和 Office 2000的硬件设备列表。
SELECT AssetID, AssetDetails.query('
<Asset>
{
element UserName { data(/AssetInfo/UserInfo/Name) },
element ComputerName { data(/AssetInfo/SystemInfo/ComputerName) },
element OS { data(/AssetInfo/SystemInfo/OS) }
}
</Asset>
') as Result
FROM [Assets]
WHERE Category = 'Hardware'
AND AssetDetails.exist('/AssetInfo/SystemInfo/OS/text()[contains(.,"Windows XP")]') = 1
AND AssetDetails.exist('/AssetInfo/SoftwaresInstalled/Software/text()[contains(.,"Office 2000")]') = 1
引用:
结论
本文为对SQL Server 2005中XQuery的基本知识感兴趣的读者提供学习信息。SQL Server 2005中,XQuery语言的不同特性,包括不支持的特性,都在本文中进行了讨论。文章中也示范了XQuery语言不同特性的应用实例。文中的XQuery应用场景可以帮助用户确定何种情况下适宜使用XQuery。
更多信息:
《XML在微软SQL Server 2005中的最佳实践》(XML Best Practices for Microsoft SQL Server 2005)
《微软SQL Server 2005对XML的支持》(XML Support in Microsoft SQL Server 2005)
《XML数据类型的性能优化》(Performance Optimizations for the XML Data Type )
引用:
版权说明
本文只是一个初步的文档,在软件随后发布的最终商业版中,本文描述的部分内容可能会有所变化。
包含在本文中的内容,代表了微软公司在这些问题上最新的观点,直到正式发布软件为止。因为微软必须根据市场条件的变化做出响应,因此本文不应作为微软任何部门的承诺,并且微软不能保证在正式版本发布后,本文中任何信息的准确性。
这个白皮书仅用于提供信息之目的。微软公司对这个文件的信息不做任何担保、明示、暗示或者法律相关责任。
遵守所有适用的版权法是用户的义务。在不限制版权法所规定权利的前提下,未经微软公司明确的书面许可,不得以任何目的、任何形式或任何手段(电子、机械、影印、录制或其他手段)对本文档的任何部分进行复制、储存或引入检索系统、或传播。
Microsoft 可能已拥有与本文档中涉及的主题相关的专利、专利申请、商标、版权或其他知识产权。除非得到 Microsoft 的明确书面许可协议,提供本文档并不表示授予使用这些专利、商标、版权或其他知识产权的任何许可。
除非另作说明,否则文中描述的公司、组织、产品、域名、电子邮件地址、图标、人员、地点和事件都是虚构的,无意联系或暗示与任何真实的公司、组织、产品、域名、电子邮件地址、图标、人员、地点和事件有所联系。
©2005 微软公司。版权所有。
Microsoft和ActiveX是微软公司在美国和/或其他国家(地区)的注册商标或商标。
此处涉及的真实公司名称和产品名称可能是其各自所有者的商标。
如果有来生,一个人去远行,看不同的风景,感受生命的活力。。。