IT技术网www.itjs.cn

当前位置:首页 > 数据库 > SQl Server > Microsoft SQL Server 2005 中的 XML 支持(1)

Microsoft SQL Server 2005 中的 XML 支持(1)

发布时间:2014-08-05 00:00 来源:未知

一、简介

可扩展标记语言 (XML) 作为一种与平台无关的数据表示形式已被广泛采用。它对于在松散耦合且完全不同的系统,以及各种企业到企业 (B2B) 应用和工作流范畴内交换信息是很有用的。数据交换已成为 XML 技术的主要驱动力之一。.

XML 在企业应用程序中的使用正日益广泛,它主要用于对半结构化和非结构化数据进行建模。文档管理就是这样的一种应用程序。像电子邮件这样的文档是半结构化性质的。假如文档以 XML 的形式存储在数据库服务器中,就可以开发功能强大的应用程序来根据文档内容检索文档、查询部分内容(例如查找标题包含单词“背景”的部分),以及查询文档聚合。假如存在能够生成和使用 XML 的应用程序,则这样的方案就变得可行了。例如,Microsoft Office 2003 系统允许用户以 XML 标记的形式生成 Word、Excel、Visio 和 Infopath 文档。

为什么使用关系数据库来存储 XML 数据?

将 XML 数据存储在关系数据库中会给数据管理和查询处理带来好处。SQL Server 提供了强大的查询和修改关系数据的能力,而且已经扩展到查询和修改 XML 数据。这使得可以利用在过去的版本上所进行的投资,就如同基于成本的优化和数据存储领域一样。例如,关系数据库中的索引技术已经广为人知,而且已经扩展到用于索引 XML 数据,这样就可以使用基于成本的决策来优化查询。

XML 数据可以与现有的关系数据和 SQL 应用程序进行互操作,这样就可以在需要进行数据建模而又不破坏现有的应用程序的系统中引入 XML。数据库服务器还提供了管理功能来管理 XML 数据(例如备份、恢复和复制)。

这些功能促进了对 SQL Server 2005 中的原生 XML 支持的需求,从而解决了不断增加的 XML 使用的问题。SQL Server 2005 中的 XML 支持将给企业应用程序开发带来好处。

下面几部分将概述 SQL Server 2000 和 2005 中的 XML 支持,描述一些推动 XML 使用的方案,并且详细讨论服务器端和客户端的 XML 功能集。

SQL Server 2000 中的 XML 支持

这一部分简要概述了 SQL Server 2000 中的 XML 支持,以及随后发布的 SQLXML 客户端编程平台 Web 版,它提供了丰富的支持来将关系数据映射到 XML 数据或将 XML 数据映射到关系数据。

服务器端支持

在服务器上,XML 数据可以从表生成,并通过在 SELECT 语句中使用 FOR XML 子句来查询结果。这对于数据交换和 Web 服务应用程序是很理想的。FOR XML 的逆函数是一个名为 OpenXML 的关系行集合生成器函数;它通过求 XPath 1.0 表达式的值来从 XML 数据提取值,并将其放到行集合的列中。应用程序使用 OpenXML 来“切碎”传入 XML 数据,并将其存放到表中,或者用于通过 T-SQL 语言进行的查询。

客户端支持

SQL Server 2000 对客户端编程的支持称为 SQLXML。这项技术的核心是 XML 视图,它是 XML 架构和关系表之间的双向映射。SQL Server 2000 只支持 XDR 架构的映射,尽管在后续的 Web 版中增加了对 XSD 的支持。XML 视图允许使用 XPath 1.0 的一个子集来进行查询,其中,可以使用映射将路径表达式转换成底层表中的 SQL 查询,并且将查询结果打包成 XML 结果。

SQLXML 还支持您创建 XML 模板,这使得您可以创建带有动态部分的 XML 文档。在 XML 文档中,您可以嵌入 FOR XML 查询和/或映射查询之上的 XPath 1.0 表达式。在执行 XML 模板时,可以用查询的结果来替换查询块。通过这种方式,您可以创建带有某些静态内容和某些数据驱动的动态内容的 XML 文档。

在SQL Server 2000 中,有两种访问 SQLXML 功能的主要方法:

SQLXMLOLEDB Provider。SQLXMLOLEDB Provider 是一个 OLE DB 提供程序,它通过 ADO 公开 Microsoft SQLXML 功能。

HTTP 访问。SQL Server 2000 中的 SQLXML 功能也可以使用 SQLXML ISAPI 过滤器通过 HTTP 进行访问。通过使用我们的配置工具,您可以建立网站来检索传入请求,从而通过 HTTP 执行 XML 模板、XML 视图之上的 FOR XML 和 XPath 1.0 语句。

XML 支持的局限性

服务器和客户端编程平台为基于表格和 XML 数据之间的映射生成和使用 XML 数据提供了丰富的支持。这能够相当好地处理结构化 XML 数据。在 SQLXML 中,查询语言是 XPath 1.0 的一个子集,并且有一些局限性。例如,不支持 descendant-or-self 轴 (//)。因此,在开发某些解决方案时会存在一定的限制。例如,不保存 XML 文档顺序,而这对于像文档管理这样的应用程序来说是非常关键的。此外,还不支持递归的 XML 架构。尽管存在这样一些局限性,但是客户端 SQLXML 和服务器 XML 功能还是在应用程序开发中得到了广泛的使用。SQL Server 2005 解决了许多这样的限制,扩展了关系 XML 交换功能,并且还提供了原生 XML 支持。

SQL Server 2005 中的 XML 支持概述

这一部分简要概述了 SQL Server 2005 中增加的新的 XML 支持,它是通过.NET 框架 V2.0 中的支持和本机客户端数据访问(如 OLE DB)进行补充的。

XML 数据类型

XML 数据模型具有一些特性,这些特性使得映射到关系数据模型非常困难,假如不是完全不可能的话。XML 数据具有可以递归的层次结构;关系数据库提供对层次数据(建模为外键关系)的弱支持。文档顺序是 XML 实例的固有属性,并且必须保存在查询结果中。这与关系数据形成了对比,关系数据是无序的,必须通过附加的排序列来强制进行排序。在查询时重新组合结果是很费力的,因为实际的 XML 架构将 XML 数据分解到大量的表中。

SQL Server 2005 引入了一种称为 XML 的本机数据类型。用户可以创建这样的表,它在关系列之外还有一个或多个 XML 类型的列;此外,还允许带有变量和参数。为了更好地支持 XML 模型特征(例如文档顺序和递归结构),XML 值以内部格式存储为大型二进制对象 (BLOB)。

SQL Server 2005 提供了 XML 架构集合,可以将其作为一种方法来以元数据的形式管理 W3C XML 架构。XML 数据类型可以与 XML 架构集合相关联,以便对 XML 实例强加架构限制。当 XML 数据与 XML 架构集合相关联时,它称为类型化的 XML;否则,就称为非类型化的 XML。在一个框架中可以同时容纳类型化的 XML 和非类型化的 XML,保留 XML 数据模型,并且强制采用 XML 语义进行查询处理。底层关系基础结构被广泛用于这一目的。它支持关系数据和 XML 数据之间的互操作,这为更广泛地采用 XML 功能开辟了道路。

XML 数据类型查询和数据修改

可以使用 T-SQL SELECT 语句来检索 XML 实例。在 XML 数据类型中提供了五种内置的方法来查询和修改 XML 实例。

XML 数据类型的方法接受 XQuery,它是一种新出现的 W3C 标准语言(目前处于 Last Call(最后请求)状态),并且包括导航语言 XPath 2.0。也可以使用一种语言来修改 XML 数据,比如添加或删除子树和更新标量值。与一大组函数一起,嵌入式的 XQuery 和数据修改语言为处理 XML 数据提供了丰富的支持。

XQuery 类型系统与 W3C XML 架构类型是一致的。大多数 SQL 类型与 XQuery 类型系统是兼容的(例如,小数)。少数类型(例如,xs:duration)是以内部格式存储的,并且可以通过适当的解释来与 XQuery 类型系统兼容。

编译阶段检查 XQuery 表达式和数据修改语句的静态类型的正确性,并且在类型化 XML 的情况下使用 XML 架构来进行类型推理。假如表达式由于类型安全冲突而在运行时失败,则会产生静态类型错误。

XML 索引

查询执行在运行时处理每个 XML 实例;假如 XML 值比较大或需要在表的许多行中对查询进行求值,则查询执行就会变得非常费时。因此提供了一种索引 XML 列的机制来加速查询。

B+ 树广泛用于建立关系数据的索引。XML 列的主 XML 索引在该列中 XML 实例的所有标记、值和路径上都创建一个 B+ 树索引。通过这种方式,可以有效地对 XML 数据中的查询进行求值,并且在保留文档顺序和文档结构的同时从 B+ 树重新组合 XML 结果。

可以在 XML 列中创建次 XML 索引来加速不同类别的常见查询:PATH 索引(用于基于路径的查询)、PROPERTY 索引(用于属性包的情况)和 VALUE 索引(用于基于值的查询)。

XML 架构处理

XML 列、变量和参数可以根据 XML 架构的集合(它可能与另一个集合相关(例如,通过使用 )或不相关)有选择地进行类型化。每个类型化的 XML 实例都从它遵循的 XML 架构集合指定目标命名空间。数据库引擎在数据分配和修改时根据 XML 架构来验证实例的有效性。

XML 架构信息用于存储和查询优化。类型化的 XML 实例在内部的二进制表示中包含类型化的值,这与 XML 索引中的一样。通过这种方式,可以有效地处理类型化 XML 数据。

关系数据和 XML 数据的集成

用户可以在同一个数据库中存储关系数据和 XML 数据。简而言之,数据库引擎除了知道如何遵循关系数据模型之外,还知道如何遵循 XML 数据模型。在升级到 SQL Server 2005 之后,关系数据和 SQL 应用程序仍然能够正常工作。在服务器上,可以将驻留在文件和文本或图像列中的 XML 数据移到 XML 数据类型的列中。通过使用 XML 数据类型的方法,可以建立 XML 列的索引,并对其进行查询和修改。

数据库利用现有的关系基础结构和引擎组件(例如存储引擎和查询处理器)来进行 XML 处理。例如,XML 索引可以创建 B+ 树,而且可以在 Showplan 输出中查看查询计划。通过集成到关系框架中,数据管理功能(如备份/恢复和复制)可用于 XML 数据。同样地,新的数据管理功能(如数据库镜像和快照隔离)可以处理 XML 数据类型,以提供无缝的用户体验。

结构化数据应该存储在表和关系列中。当应用程序需要执行细粒度查询和数据修改时,对于使用 XML 的半结构化数据和标记数据,XML 数据类型是比较合适的选择。

FOR XML 和 OpenXML 增强

现有的 FOR XML 功能已经在几个方面得到了增强。它能够处理 XML 数据类型的实例和其他新的 SQL 类型,例如 [n]varchar(max)。

新的 TYPE 指令生成的 XML 数据类型实例可以分配给 XML 列、变量或参数,也可以使用 XML 数据类型的方法进行查询。这允许嵌套 SELECT ...FOR XML TYPE 语句。

PATH 模式允许用户指定出现列值的 XML 树中的路径,并且与上述嵌套一起使用时比 FOR XML EXPLICIT 更易于编写。

XSINIL 指令与 ELEMENTS 一起使用,可以将 NULL 映射到带有属性 xsi:nil="true" 的元素。另外,新的 ROOT 指令还允许在所有的 FOR XML 模式下指定根节点。新的 XMLSCHEMA 指令生成 XSD 内联架构。

OpenXML 的功能增强包括在 sp_preparedocument 中接受 XML 数据类型以及在行集中生成 XML 和新的 SQL 类型的列。

对 XML 数据类型的客户端访问

客户端可以通过几种方式访问服务器中的 XML 数据。使用 ODBC 和 OLE DB 的本机 SQL 客户端访问以 Unicode 字符串的形式传递 XML 数据类型。OLE DB 还提供对流 Unicode 数据的 XML 数据类型的 ISequentialStream 访问。

托管访问通过 .NET 框架 V2.0 中的 ADO.NET 将 XML 数据作为一个名为 SqlXml 的新类进行传递。它支持一个名为 CreateReader() 的方法,该方法返回 XmlReader 实例来读取返回的 XML。同样地,数据集能够将 XML 数据类型的实例加载到中间层的列,中间层可以作为 XML 文档进行编辑,并且重新保存到 SQL Server。这两者都支持对服务器发出 SQL 查询,以检索在中间层操作的 XML 列。

在 SQL Server 2005 中,可以使用直接对 HTTP 终结点进行的SOAP 访问来查询、检索和修改 XML 数据。

本机和托管客户端技术都提供了新的接口来检索类型化 XML 列的 XML 架构集合。

带有 XQuery 的客户端 XML 支持

除了使用 ADO.NET 来检索表并将结果存放到关系数据集中之外,您还可以使用 XQueryCommand 类将关系数据直接加载到中间层的 XML 文档。该类提供了一个中间层 XQuery 处理器,它能够将 T-SQL 语句作为 XQuery 语言的一部分嵌入。通过这种方式,您可以查询本地 XML 文件,也可以从 SQL Server 检索数据(直接从表或者通过数据库存储程序),再将数据构造成 XML 格式,然后将其加载到中间层 XML 文档。这大大简化了 SQL Server 中的查询,将查询结果构造成特定的 XML 格式,并使其流向业务合作伙伴,而不需要将数据加载到数据集来执行数据转换。

二、推动 XML 存储方案

XML 数据变得越来越普遍.它可以表示客户数据,带有或不带有描述数据的 XML 架构均可。XML 数据和 XML 架构需要在一起进行管理。实际应用程序的 XML 架构通常比较复杂,因而将这样的 XML 架构映射到表和列是一项复杂的任务。当 XML 架构改变或新的架构加入系统时,长时间维护这样的映射是一件很麻烦的事情。通常,XML 数据存储在文件系统或数据库服务器的文本列中。文本列有数据管理方面的好处(例如,复制和备份/恢复),但是不提供任何基于数据的 XML 结构的查询支持。在具有原生 XML 支持的情况下,使用 XML 进行应用程序开发变得更快。

自定义属性管理

一些应用程序(例如用户界面软件)允许用户从一组固定的属性中进行选择。而其他应用程序则允许用户定义他们自己感兴趣的属性。假如这样的自定义属性是以 XML 的格式存储的,它们就可以很好地进行管理。应用程序可以支持标量属性以外的属性:

可以支持对象中的多值属性,例如,多个电话号码。

可以支持复杂属性,例如,一个文档的作者属性可能是该作者的联系信息。

可以将对象属性存储在 XML 数据类型的列中并建立索引,从而提高查询处理的效率。

数据交换和工作流

XML 允许采用平台无关的方式在应用程序之间交换数据。可以使用 XML 标记将这样的数据建模为消息。代替不断地分割和生成 XML 消息,以 XML 的格式存储消息是明智的。这正是数据流所需要的。到达工作流阶段的 XML 消息携带着当前的状态。每个消息都需要进行处理,处理的进展记录在 XML 内容中(如状态改变),然后将 XML 数据转发到工作流处理的下一个阶段。消息可能是不同类型的,甚至可能是半结构化的,并且有不同的 XML 架构与它们相关联,因此将它们映射到表并不总是一件轻而易举的事。

针对不同垂直领域(如金融数据和地理空间数据)的基于 XML 的标准正在形成。这些标准根据可以查询和更新的实例数据来描述数据的结构。通常,实际数据采用的是二进制形式,而 XML 数据提供关于它们的元数据信息。

举一个简单的例子,为了将一个输入参数表传送到存储程序或函数,应用程序首先将数据转换成 XML,然后将其作为一个 XML 数据类型参数进行传递。在存储过程或函数内,从 XML 参数重新生成行集。

文档管理

假设有一个呼叫中心,它采用 XML 文档来维护患者记录和谈话。当患者发起呼叫时,呼叫中心希望恢复前面的谈话以设置传入呼叫的情景。这可以通过查询 XML 标记实现,从而给应用程序带来好处。此外,还可以方便地检索以前谈话的细节并记录当前的谈话。

像电子邮件之类的文档本质上是半结构化的。带有 XML 标记的文档正变得越来越容易创建,例如,使用 Office 2003。可以将这些 XML 文档存储在 XML 列中,并建立索引,进行查询和更新。因此,通过利用原生 XML 支持,开发人员可以做更多的事情。

三、SQL Server 2005 中的服务器端 XML 处理

SQL Server 2005 支持包括提供一个数据库,在这个数据库中,您可以存储关系数据和 XML 数据。

XML 数据类型

您可以使用普通的 CREATE TABLE 语句来创建带有 XML 列的表。然后就可以采用一种特别的方式来建立 XML 的索引。

非类型化的 XML

SQL Server 2005 XML 数据类型实现了 ISO SQL-2003 标准 XML 数据类型。因此,它不仅可以存储格式良好的 XML 1.0 文档,而且可以存储所谓的 XML 内容片段(带有文本节点和任意数目的顶层元素)。在对数据进行格式良好性检查时,并不要求将 XML 数据类型绑定到 XML 架构,但是格式不规范的数据将被拒绝。

当架构不是已知先验的 并且因此而导致基于映射的解决方案不可能实现时,就可以使用非类型化的 XML。假如架构是已知的,但映射到关系数据模型非常复杂并且难于维护,或者存在多个架构而且这些架构是后来根据外部要求绑定到数据的,也可以使用非类型化的 XML。

例:表中非类型化的 XML 列

下面的语句创建一个名为“docs”的表,该表带有整型主键“pk”和非类型化的 XML 列“xCol”:

CREATE TABLE docs (pk INT PRIMARY KEY, xCol XML not null)

也可以创建一个包含多个 XML 列或关系列、带主键或不带主键的表。

类型化的 XML

假如 XML 架构集合中有描述 XML 数据的 XML 架构,就可以将 XML 架构集合与产生类型化 XML 的 XML 列相关联。可以使用 XML 架构来验证数据的有效性,在编译查询和数据修改语句时执行比非类型化的 XML 更精确的类型检查,以及优化存储和查询处理。

类型化的 XML 列、参数和变量可以存储 XML 文档或内容,可以在声明时将其指定为一个选项(分别为DOCUMENT 或 CONTENT,默认为 CONTENT)。此外,还必须提供 XML 架构集合。假如每个 XML 实例都正好有一个顶层元素,则指定 DOCUMENT;否则,使用 CONTENT。查询编译器在类型检查中使用 DOCUMENT 标记来推理 singleton 顶层元素。

例:表中的类型化 XML 列

XML 列、变量和参数可以绑定到一个 XML 架构集合(请参阅本文后面的“XML 架构处理”一节以获得更多的详细信息和示例)。假定 myCollection 代表这样一个集合。下面的语句创建一个表 XmlCatalog,带有使用 myCollection 进行类型化的 XML 列文档。类型化的 XML 列还被指定为接受 XML 片段,而不只是 XML 文档。

CREATE TABLE XmlCatalog ( 
   ID INT PRIMARY KEY, 
   Document XML(CONTENT myCollection))

约束 XML 数据类型的列

除了类型化一个 XML 列之外,还可以在类型化和非类型化的 XML 数据类型的列中使用关系(列或行)约束。大部分 SQL 约束同样可应用于 XML 列,值得注意的唯一例外是主键和外键约束,因为 XML 数据类型的实例是不兼容的。因此,可以指定 XML 列可为空或不可为空,提供默认值,并且在列中定义 CHECK 约束。例如,非类型化的 XML 列可以有 CHECK 约束来验证存储的 XML 实例是否符合 XML 架构。

在下列条件下使用约束:

业务规则不能用 XML 架构表示。例如,花店的交付地址必须在其业务场所 50 英里的范围内,这可以编写成 XML 列中的一个约束条件。该约束条件可以包括 XML 数据类型的方法。

约束条件涉及表中其他的 XML 或 非 XML 列。这样的一个例子就是,使 XML 实例中存在的 Customer (/Customer/@CustId) 的id 与整型 CustomerID 列中的值相匹配。

例:约束 XML 列

要确定<book>的<author>的<last-name> 不同于<author>的<first-name> ,可以指定下列 CHECK 约束:

CREATE TABLE docs (pk INT PRIMARY KEY, xCol XML not nullCONSTRAINT CK_name CHECK (xCol.exist('/book/author[first-name = last-name]') = 0))

文本编码

SQL Server 2005 将 XML 数据存储为 Unicode (UTF-16)。从服务器检索的XML 数据,其结果也是 UTF-16 编码的。假如想要采用一种不同的编码方式,就需要在检索数据之后进行必要的转换,转换的方法有两种,一种是通过强制类型转换,另一种是在中间层执行转换。例如,可以在服务器上将 XML 数据强制转换成 varchar 类型,在这种情况下,数据库引擎会通过 varchar 类型的排序所确定的编码方式来序列化 XML。

存储 XML 数据

可以通过多种方式为 XML 列、参数或变量提供 XML 值。

作为隐式转换到 XML 数据类型的字符或二进制 SQL 类型。

作为文件的内容。

作为 XML 发布机制 FOR XML 的输出(带有生成 XML 数据类型实例的 TYPE 指令)

对提供的值进行格式良好性检查,并且允许存储 XML 文档和 XML 片段。假如数据没有通过格式良好性检查,则拒绝它,并发出一个适当的错误消息。

对于类型化的 XML,需要检查提供的值是否符合已注册到类型化 XML 列的 XML 架构集合的 XML 架构。假如该 XML 实例没有通过这种有效性验证,则拒绝它。此外,仅当 CONTENT 允许提供 XML 文档和内容时,类型化的 XML 中的 DOCUMENT 标记才将所接受的值限制为 XML 文档。

例:将数据插入非类型化的 XML 列

下列语句在表 docs 中新插入一行,其中在整型的 pk 列插入的值为 1,而在 XML 列插入的是 实例。 数据(作为字符串提供)被隐式地转换为 XML 数据类型,并且在插入的过程中进行格式良好性检查。

INSERT INTO docs VALUES (1, 
'<book genre="security" publicationdate="2002" ISBN="0-7356-1588-2">
   <title>Writing Secure Code</title>
   <author>
      <first-name>Michael</first-name>
      <last-name>Howard</last-name>
   </author>
   <author>
      <first-name>David</first-name>
      <last-name>LeBlanc</last-name>
   </author>
   <price>39.99</price>
</book>')

例:将来自文件的数据插入非类型化的 XML 列

如下所示的 INSERT 语句使用 OPENROWSET 读取文件 C:\temp\xmlfile.xml 的内容作为 BLOB。在表 docs 中新插入一行,值 10 作为主键,而 BLOB 作为 XML 列 xCol。格式良好性检查出现在文件内容分配到 XML 列时。

INSERT INTO docs 
SELECT 10, xCol
FROM    (SELECT * FROM OPENROWSET 
      (BULK 'C:\temp\xmlfile.xml',
      SINGLE_BLOB) AS xCol) AS R(xCol)

例:将数据插入类型化的 XML 列

类型化的 XML 列需要 XML 实例数据指定用于对其进行类型化的 XML 架构的目标命名空间(该命名空间可以为空)。在下面的示例中,这是通过命名空间声明 xmlns=http://myDVD 来实现的。

INSERT XmlCatalog VALUES(2, 
'< xml version="1.0" >
<dvdstore xmlns="http://myDVD">
  <dvd genre="Comedy" releasedate="2003">
    <title>My Big Fat Greek Wedding</title>
    <price>19.99</price>
  </dvd>
</dvdstore>')

例:存储使用带有 TYPE 指令的 FOR XML 生成的 XML 数据

通过 TYPE 指令增强的 FOR XML 可以生成像 XML 数据类型实例这样的结果。结果 XML 可以分配到 XML 列、变量或参数。在下面的语句中,使用 FOR XML TYPE 生成的 XML 实例被分配给 XML 数据类型的变量 @xVar。可以使用 XML 数据类型的方法来查询变量。

DECLARE @xVar XML
SET     @xVar = (SELECT * FROM docs FOR XML AUTO,TYPE)

存储表示

XML 数据类型的实例存储在内部的二进制表示中,该表示是可流化的,并且经过了优化,从而可以更有效地进行解析。标记映射到整型值,而映射的值存储在内部表示中。这也产生了一些数据的压缩。

对于非类型化的 XML,节点值存储为 Unicode (UTF-16) 字符串,因此需要进行运行时类型转换才能执行操作。例如,为了求谓词 /book/price > 9.99 的值,必须将该书的价格值转换为小数。而对于类型化的 XML,值的编码类型为在 XML 架构中指定的类型。这使得数据的解析更加有效,并且还不必进行运行时转换。

存储的二进制形式被限制为每 XML 实例 2 GB,这可以适应大部分的 XML 数据。此外,XML 层次的深度还被限制为 128 层。

XML 数据的信息集内容被保留。但是,它不可能是与文本 XML 一模一样的副本,因为下列信息没有保留:无关紧要的空白、属性的顺序、命名空间前缀和 XML 声明。

数据建模考虑事项

通常,关系数据类型和 XML 数据类型的列的组合比较适合数据建模。可以将 XML 数据中的一些值存储在关系列中,而将其余的值或全部的 XML 值存储在 XML 列中。这可以产生更好的性能和锁定特性。

对于 singleton 值(即单值属性),XML 数据中的值可以提升为同一个表中的计算列。多值属性需要单独的属性表,该表必须使用触发器进行填充和维护。查询需要直接针对属性表进行编写。

对于锁定和更新特性,存储在 XML 列中的 XML 数据的粒度是至关重要的。SQL Server 将相同的锁定机制用于 XML 和非 XML 数据。假如粒度比较大,则在多用户的情况下,为了更新而锁定大的 XML 实例会引起吞吐量的下降。而另一方面,严格的分解会失去对象的封装性并增加重新组合的成本。

查询和修改 XML 数据

查询存储在 XML 列中的 XML 实例需要解析列中的二进制 XML 数据。与解析文本形式的 XML 数据相比,解析二进制的 XML 要快得多。XML 索引避免了重新解析,这将在“建立 XML 数据的索引”一节中进行讨论。

XML 数据类型中的方法

假如感兴趣,可以检索全部 XML 值,也可以检索部分 XML 实例。这可以使用四个 XML 数据类型的方法来实现:query()value()exist()nodes(),它们接受 XQuery 表达式作为参数。第五个方法 modify() 允许修改 XML 数据并接受 XML 数据修改语句作为输入。

query() 方法用于提取 XML 实例的部分。XQuery 表达式求值为一个 XML 节点列表。以这些节点中的每一个为根的子树按照文档顺序返回。结果类型为非类型化的 XML。

value() 方法从 XML 实例提取标量值。它返回 XQuery 表达式所求值的节点的值。该值被转换为 value() 方法的第二个参数所指定的 T-SQL 类型。

exist() 方法用于对 XML 实例进行存在性检查。假如 XQuery 表达式求值为非空节点列表,则返回 1;否则,返回 0。

nodes() 方法产生特定 XML 数据类型的实例,每个实例都将其上下文设置为 XQuery 表达式所求值的不同节点。特定的 XML 数据类型支持 query()value()nodes()exist() 方法,并且可以用于 count(*) 聚合和 NULL 检查。所有其他的使用都会产生错误。

modify() 方法允许修改 XML 实例的某些部分,例如添加或删除子树,或者更新标量值(如将书的价格从 9.99 替换为 39.99)。

例:使用 query() 方法

考虑下面的表 docs 的 XML 列 xCol 中的查询,它提取 元素下的任何位置的 id 为 123 的 元素。该查询也从整型主键列检索值。SELECT 列表中的 query() 方法会为生成 元素序列的表中的每一行进行求值, 元素及其子树是按照文档顺序进行检索的。对于每个 XML 实例,假如没有 id 为 123 的 元素或者其下没有 元素,则不会返回结果,也就是说,query() 方法的返回值为 NULL。

SELECT pk, xCol.query('/doc[@id = 123]//section')   
FROM   docs

NULL 返回值可以在外部 SELECT 语句中过滤掉。或者也可以使用 exist() 方法,如下一个示例所示。

例:使用 exist() 方法

考虑下面的查询,它在表 docs 的 XML 列 xCol 中包括 query()exist() 方法。exist() 方法求路径表达式 /doc[@id = 123] 的值,检查是否存在顶层 元素,该元素具有一个称为 id 的属性,且其值为 123。对于每个这样的行,SELECT 子句中的 query() 方法都会进行求值;在这个示例中,query() 方法在 元素下的任何位置都产生一个 元素序列。从 exist() 方法返回 0 的任何行都会被忽略。

SELECT xCol.query('/doc[@id = 123]//section')   
FROM   docs
WHERE  xCol.exist ('/doc[@id = 123]') = 1

例:使用 value() 方法

下面的查询使用 value() 方法以 Unicode 字符串的形式提取文档第三部分的标题。结果的 SQL 类型的 nvarchar(max) 被指定为 value() 方法的第二个参数。XQuery 函数 data() 从 节点提取标量值。

SELECT xCol.value( 'data((/doc//section[@num = 3]/heading)[1])', 'nvarchar(max)') FROM docs

XQuery 语言

众多的 XML 都来源于存储在文件系统、Web 服务或配置文件中的 Office 文档。事实上,以 XML 格式或作为虚拟 XML 文档生成的数据正在不断地增加。为了处理这些数量越来越多的数据,一种强大的查询语言 XQuery 应运而生。在 XQuery 语言规范(位于 http://www.w3.org/TR/xquery)中将选择 XQuery 的理由描述为:

一种巧妙地使用 XML 结构的查询语言,可以跨各种数据表示查询,而不管这些数据是物理存储在 XML 中,还是通过中间件被视为 XML。该规范描述了一种称为 XQuery 的查询语言,它旨在能广泛应用于许多类型的 XML 数据源。

XQuery 旨在满足 W3C XML 查询工作组 XML 查询 1.0 要求和 XML 查询用例中的用例所确定的要求。它是一种旨在使查询简洁易懂的语言。它还相当地灵活,可以查询大范围的 XML 信息源,其中包括数据库和文档。

XQuery 还可以概括为如下表述:XQuery 语言之于 XML 正如 SQL 语言之于关系数据库。

内嵌于 T-SQL 的 Xquery 子集(位于 http://www.w3.org/TR/xquery/)是一种支持查询 XML 数据类型的语言。这种语言正在由 Worldwide Web Consortium (W3C) 进行开发(目前处于最后请求状态),所有主要的数据库厂商(包括 Microsoft 在内)都参与其中。我们的实现与 2003 年 11 月发布的 XQuery 草案是一致的。

XQuery 将 XPath 2.0 作为导航语言包括在内。SQL Server 2005 的 XQuery 实现提供了用于遍历节点 (for)、节点检查 (where)、返回值 (return) 和排序 (order by) 的构造。它也提供了用于在查询过程中重新进行数据构形的元素构造。

SQL Server 2005 还提供了用于 XML 数据类型的数据修改 (DML) 的语言构造(请参阅下面的“数据修改”一节以获得更多信息)。下面的示例演示了如何将 XQuery 用于 XML 数据类型。

例:使用 XQuery 中丰富的语言构造

下面的查询显示了如何一起使用几个 XQuery 语言构造。它从 id 为 123 的文档返回区域号为 3 和更高的区域中的标题,并将其包装在新标记 中。

SELECT pk, xCol.query('
   for $s in /doc[@id = 123]//section
   where $s/@num >= 3
   return <topic>{data($s/heading)}</topic>')   
FROM docs

“for”遍历 id 为 123 的<doc> 元素下的所有<section> 元素,并且将每个这样的<section> 元素绑定到变量 $s。“where”确保区域号(<section> 元素的 @num 属性)为 3 或更高。该查询按照文档顺序返回区域<heading> 中的值,并且将其包装在一个称为<topic> 的构造元素中。

查询编译和执行

SQL 语句是由 SQL 解析器解析的。当它遇到 XQuery 表达式时,它就会跳转到 XQuery 编译器,然后编译 XQuery 表达式。这会产生一个查询树,并将其嫁接到整个查询的查询树上。

整个查询树会进行查询优化并产生物理查询计划,该计划是根据基于成本的评估得出的。Showplan 输出显示了大部分关系运算符和一些新的运算符(例如,用于 XML 处理的 UDX)。

查询执行与关系框架中的其余部分一样,都是面向元组的。在表 docs 的每一行上都求 WHERE 子句的值;这需要在运行时解析 XML BLOB 以求出 XML 数据类型方法的值。假如条件满足,则锁定行,然后在行中求 SELECT 子句的值。结果以 XML 数据类型的形式产生(用于 query() 方法),并且转换成指定的目标类型(用于 value() 方法)。

而假如该行不满足 WHERE 子句中的条件,它就会被忽略,执行转到下一行。

XML 数据修改

SQL Server 2005 提供了用于数据修改的构造作为对 XQuery 的一个扩展。子树可以在指定的节点之前或之后插入,或者作为最左边或最右边的子节点插入。此外,子树也可以插入到父节点,在这种情况下,它成为父节点最右边的子节点。属性、元素和文本节点插入都支持。

支持删除子树。在这种情况下,整个子树就从 XML 实例中被移除。

标量值可以用新的标量值进行替换。

例:将子树插入 XML 实例

这个示例显示了 modify() 方法的使用,它将一个新的<section> 元素插入编号为 1 的 元素的右边。

UPDATE docs SET xCol.modify('
  insert 
   <section num="2">
         <heading>Background</heading>
</section>                
  after (/doc/section[@num=1])[1]')

例:将这本书的价格更新为 $49.99

下面的更新语句将 ISBN 为 1-8610-0311-0 的书的<price> 替换为 $49.99。该 XML 实例是通过 XML 架构 http://myBooksinstance 进行类型化的,因而就有了 XML 数据修改语句中的命名空间声明。

UPDATE XmlCatalog
SET    Document.modify ('
    default namespace = "http://myBooks"
    replace value of (/bookstore/book[@ISBN=
"1-8610-0311-0"]/price)[1] with 49.99')

类型检查和静态错误

XQuery 引入了类型检查。编译阶段检查 XQuery 表达式和数据修改语句的静态类型正确性,并且将 XML 架构用于类型推理(在类型化 XML 的情况下)。假如表达式在运行时由于类型安全冲突而失败,则会产生静态类型错误。静态错误的例子有:将一个字符串与一个整数相加、在操作需要单值的地方接收一个值序列,以及查询不存在的节点来查找类型化数据。对于因类型不匹配而导致的静态错误,显式转换到正确的类型是一种变通方法。XQuery 运行时错误被转换成空序列。

假如编译器不能确定在运行时是否保证有 singleton 元素,则需要 singleton 元素的定位步骤、函数参数和运算符(例如,eq)就会返回错误。这种问题常常因非类型化的数据而产生。例如,查找属性需要 singleton 父元素;顺序选择单个的父节点就足够了。

例:value() 方法中的类型检查

下面的查询是在非类型化的 XML 列中进行的,它需要 //author/last-name 中的顺序规范,因为 value() 方法需要 singleton 节点作为第一个参数。假如没有,则编译器就不能确定在运行时是否只出现一个<last-name> 节点。

SELECT xCol.value('(//author/last-name)[1]', 'nvarchar(50)') LastName
FROM   docs

通过求 node()-value() 组合的值来提取属性值可以不需要顺序规范,如下一个示例所示。

例:已知的 singleton 元素

如下所示的 nodes() 方法为每个<book> 元素生成单独的行。对<book> 节点求值的 value() 方法提取 @genre 的值,@genre 作为一个属性,是 singleton 元素。

SELECT nref.value('@genre', 'varchar(max)') LastName
FROM   docs CROSS APPLY xCol.nodes('//book') AS R(nref)

跨域查询

假如数据驻留在关系数据类型的列和 XML 数据类型的列的组合中,就可能需要编写查询来组合关系数据处理和 XML 数据处理。通过使用带有 TYPE 指令的 FOR XML,可以将关系列和 XML 列中的数据转换成 XML 数据类型的实例,并使用 XQuery 对其进行查询。相反地,可以从 XML 值生成行集,并且使用 T-SQL 来对其进行查询,如下面的“从 XML 数据生成行集”一节所示。

编写跨域查询的一个更加方便和有效的方法是,使用 SQL 变量的值,或者使用 XQuery 或 XML 数据修改表达式中的列:

使用 sql:variable(),可以在 XQuery 或 XML DML 表达式中应用 SQL 变量的值。

通过 sql:column(),可以在 XQuery 或 XML DML 上下文中使用来自关系列的值。

这种方法允许应用程序参数化查询,如下面的示例所示。Sql:column() 的用法与前者类似,并且还带来其他的好处。正如基于成本的查询优化器所确定的,可以使用列的索引来提高效率。此外,也可以使用计算列。

XML 和用户定义的类型不允许与 sql:variable()sql:column() 一起使用。

例:使用 sql:variable() 的跨域查询

在这种查询中,<book> 元素的 ISBN 是使用 SQL 变量 @isbn 来传入的。代替使用常量,sql:variable() 提供 ISBN 的值,并且该查询可以用于搜索任何 ISBN,而不只是 ISBN 为 0-7356-1588-2 的 元素。

DECLARE @isbn varchar(20)
SET     @isbn = '0-7356-1588-2'
SELECT  xCol
FROM    docs
WHERE   xCol.exist ('/book[@ISBN = sql:variable("@isbn")]') = 1

从 XML 数据生成行集

在自定义属性管理和数据交换场景中,应用程序通常将 XML 数据的某些部分映射到行集。例如,为了将输入参数表传送到存储过程或函数,应用程序需要将数据转换成 XML,并且将其作为 XML 数据类型的参数传入。在存储过程或函数中,行集是从 XML 参数生成的。

SQL Server 2000 为此提供了 OpenXml()。它简化了从 XML 实例生成行集的过程,方法是指定行集的关系架构以及将 XML 实例内的值映射到行集中的列的方式。

另外,还可以使用 nodes() 方法来生成 XML 实例中的节点上下文,并且在 value()query()exist()nodes() 方法中使用该节点上下文来生成所期望的行集。nodes() 方法接受 XQuery 表达式,在 XML 列的每个 XML 实例中对其进行求值,并且有效地使用 XML 索引。下一个示例演示如何将 nodes() 方法用于行集生成。

例:从 XML 实例提取属性

假定要提取名字不是“David”的作者的名和姓,将其作为由两列(FirstName 和 LastName)组成的一个行集。通过使用 nodes()value() 方法,您可以达到此目的,如下所示:

SELECT nref.value('first-name[1]', 'nvarchar(50)') FirstName,
       nref.value('last-name[1]', 'nvarchar(50)') LastName
FROM   docs CROSS APPLY xCol.nodes('//author') AS R(nref)
WHERE  nref.exist('.[first-name != "David"]') = 1

在这个示例中,nodes('//author') 产生一个指向每个 XML 实例的 元素的引用的行集。作者的名和姓是通过对与这些引用有关的 value() 方法求值来获得的。要获得好的性能,需要建立 XML 列的索引,这是下一部分的主题。

建立 XML 数据的索引

XML 数据是以内部二进制形式存储的,存储容量可以达到 2 GB。每个查询在运行时一次或多次地解析表中每一行的 XML BLOB。这会使查询处理的速度变得很慢。假如在工作负荷中常常需要进行查询,则建立 XML 列的索引是有好处的,不过,这样做必须考虑在修改数据的过程中维护 XML 索引的成本。

XML 索引是通过一个新的 DDL 语句在类型化和非类型化的 XML 列中创建的。这为该列中的所有 XML 实例创建了一个 B+ 树。XML 列中的第一个索引是“主 XML 索引”。通过这个索引,可以在 XML 列中支持三种类型的次 XML 索引来加速普通类的查询,如下一节所述。

主 XML 索引

在基表(即定义 XML 列的表)的主键中,主 XML 索引需要聚集索引。它在 XML 节点的信息集项的一个子集中创建一个 B+ 树。B+ 树的列表示标记,例如元素和属性名称、节点值和节点类型。其他的列捕获 XML 数据中的文档顺序和结构,以及从 XML 实例的根节点到每个节点的路径,从而有效地对路径表达式进行求值。基表的主键在主 XML 索引中复制,以使索引行与基表行相关。

XML 架构中给定的标记和类型名称被映射为整数值,而映射值存储在 B+ 树中以优化存储。索引中的路径列按照相反的顺序(即从节点到 XML 实例的根)存储映射值的串联。当路径后缀已知时(在一个路径表达式中,如 //author/last-name),相反的表示使得可以匹配路径值。

假如对基表进行分区,则需要以相同的方式对主 XML 索引进行分区,也就是使用相同的分区函数和分区架构。

全部的 XML 实例都是从 XML 列检索的(SELECT * FROM docs 或 SELECT xCol FROM docs)。需要 XML 数据类型方法的查询使用主 XML 索引,从索引本身返回标量值或 XML 子树。

例:创建主 XML 索引

下面的语句在表 docs 的 XML 列 xCol 中创建一个名为 idx_xCol 的 XML 索引。

CREATE PRIMARY XML INDEX idx_xCol on docs (xCol)

次 XML 索引

一旦主 XML 索引创建完毕,就可以创建次 XML 索引来加速工作负荷中不同类的查询。三种类型的次 XML 索引 — PATH、PROPERTY 和 VALUE — 分别对基于路径的查询、自定义属性管理方案和基于值的查询有利。

PATH 索引在主 XML 索引的列 (path, value) 中构建 B+ 树。该路径的值是通过计算路径表达式和节点的值得出的,假如提供了一个路径值,则也可以使用所提供的值。在已知 PATH 索引开始字段的情况下,查找 PATH 索引可以加速路径表达式的求值。最常见的情况是在 SELECT 语句的 WHERE 子句中对 XML 列使用 exist() 方法。

PROPERTY 索引在主 XML 索引的列 (PK, path, value) 中创建 B+ 树,其中 PK 是基表的主键。此索引对 XML 实例中的属性值查找有利。

最后,VALUE 索引在主 XML 索引的列 (value, path) 中创建一个 B+ 树。此索引对节点的值已知但是其路径没有准确地在查询中指定的查询有利。这通常出现在祖先或自身 (descendant-or-self) 轴查询中,例如,//author[last-name="Howard"],其中, 元素可以出现在层次的任何一层。它还可以出现在“wildcard”查询中,例如 /book [@* = "novel"],其中,查询查找具有“novel”属性值的 元素。此外,VALUE 索引还可用于对类型化的 XML 进行基于值的范围扫描。

可以容纳多达 128 层的 XML 层次;在插入和修改的过程中,假如 XML 实例包含更长的路径,则会被拒绝。

类似地,可以建立一个节点值的前 128 个字节的索引;系统中可以容纳更长的值,但是不会建立索引。

例:基于路径的查找

假定下面的查询在工作负荷中是常见的:

SELECT xCol
FROM   docs
WHERE  xCol.exist ('/book[@genre = "novel"]') = 1

路径表达式 /book/@genre 和值“novel”对应于 PATH 索引的键字段。因此,类型 PATH 的次 XML 索引有助于此工作负荷:

CREATE XML INDEX idx_xCol_Path on docs (xCol)
   USING XML INDEX idx_xCol FOR PATH

例:获取对象的属性

考虑下面的查询,它从表 T 的每一行检索书的“genre”、“title”和 ISBN 属性:

SELECT xCol.value ('(/book/@genre)[1]', 'varchar(50)'),
    xCol.value ('(/book/title)[1]', 'varchar(50)'),
    xCol.value ('(/book/@ISBN)[1]', 'varchar(50)')
FROM    docs

属性索引可用于这种情况,该索引创建如下:

CREATE XML INDEX idx_xCol_Property on docs (xCol)
   USING XML INDEX idx_xCol FOR PROPERTY

例:基于值的查询

在下面的查询中,祖先或自己轴 (//) 指定了部分路径,这样,基于 ISBN 值的查询就可以从 VALUE 索引的使用中受益:

SELECT xCol
FROM    docs
WHERE    xCol.exist ('//book[@ISBN = "1-8610-0157-6"]') = 1

VALUE 索引创建如下:

CREATE XML INDEX idx_xCol_Value on docs (xCol)
   USING XML INDEX idx_xCol FOR VALUE

内容索引

可以在 XML 列中创建全文本索引;这会建立 XML 值内容的索引而忽略 XML 标记。属性值不是全文本索引的(因为它们被认为是标记的一部分),而元素标记被用作标记边界。可以在 XML 列中创建 XML 和全文本索引,并且组合使用全文本搜索和 XML 索引。使用全文本索引作为第一个筛选器来缩小选择范围,接着再应用 XQuery 进一步筛选。

使用 CONTAINS() 和 XQuery contains() 的全文本搜索有不同的语义。后者是子字符串匹配,而前者是使用词根检索的标记匹配。

例:在 XML 列中创建全文本索引

在 XML 列中创建全文本索引的步骤与在其他 SQL 类型列中创建全文本索引的步骤是非常相似的。需要在基表中有一个唯一的键列。DDL 语句如下,其中,PK__docs__7F60ED59 是该表的单列主键索引:

CREATE FULLTEXT CATALOG ft AS DEFAULT
CREATE FULLTEXT INDEX ON dbo.docs (xCol) KEY INDEX PK__docs__7F60ED59

例:组合全文搜索和 XML 查询

下面的查询检查 XML 值是否在书的标题中包含单词“Secure”:

SELECT * 
FROM   docs 
WHERE  CONTAINS(xCol,'Secure') 
AND    xCol.exist('/book/title/text()[contains(.,"Secure")]') =1

CONTAINS() 方法使用全文本索引来产生一个 XML 值的子集,它包含文档中所有的单词“Secure”。exist() 子句确保单词“Secure”出现在书的标题中。

使用 XML 索引执行查询

XML 索引可以加速查询的执行。查询总是针对 XML 列中的主 XML 索引进行编译(假如存在的话)。为整个查询(包括关系部分和 XML 部分)制订一个查询计划,该计划可以通过数据库引擎基于成本的优化器进行优化。根据查询优化器的成本估算来选择次 XML 索引。

XML 索引的目录视图

目录视图用于提供关于 XML 索引的元数据信息。目录视图 sys.indexes 包含带有索引“type”3 的 XML 索引项。“name”列包含 XML 索引的名称。

XML 索引也记录在目录视图 sys.xml_indexes 中,它包含所有的 sys.indexes 列以及一些对 XML 索引有意义的特别的列。列“secondary_type”中的值 NULL 表示主 XML 索引,值“P”、“R”和“V”分别代表 PATH、PROPERTY 和 VALUE 次 XML 索引。

可以从表值函数 sys.fn_indexinfo() 中找到 XML 索引的空间使用。它提供的信息包括所占用的磁盘页数、按字节计算的平均行大小、记录数,以及其他包括 XML 索引在内的所有索引类型的信息。这些信息可用于每个数据库分区;XML 索引使用与基表相同的分区架构和分区函数。

例:XML 索引的空间使用

SELECT sum(Pages)
FROM    sys.fn_indexinfo ('docs', 'idx_xCol_Path' , DEFAULT, 'DETAILED')

这产生在表 T 的所有分区中 XML 索引 idx_xCol_Path 所占用的磁盘页数。假如没有 sum() 函数,则结果将会返回每个分区的磁盘页使用。

XML 架构处理

在系统中,XML 架构是可选的。如前所述,没有绑定到 XML 架构的 XML 数据类型被认为是非类型化的;XML 节点值将作为 Unicode 字符串存储,而 XML 实例需要进行格式良好性检查。可以建立非类型化 XML 列的索引。

要类型化 XML,可以将 XML 数据类型与已注册到 XML 架构集合的 XML 架构相关联。新的 DDL 语句允许创建一个或多个 XML 架构可以注册到的 XML 架构集合。绑定到 XML 架构集合的 XML 列、参数或变量是根据该集合中的所有 XML 架构进行类型化的。在 XML 架构集合中,类型系统标识每个使用其目标命名空间的 XML 架构。

XML 实例中的每个顶层 XML 元素都必须指定它遵循的可能为空的目标命名空间。在插入和修改数据时,将根据每个顶层元素的目标命名空间对其进行有效性检查。二进制 XML 表示根据相关的 XML 架构信息对类型化值进行编码,并且是完整描述的,因此,与非类型化的 XML 相比,它的再解析效率更高一些。在 XML 索引中,还将对值进行适当的类型化(假如 /book/price 是在像 xs:decimal 这样的命名空间中定义的,则会存储为小数)。

在查询编译的过程中,使用 XML 架构进行类型检查,并且会因类型不匹配而引起静态错误。查询编译器还将 XML 架构用于查询优化。

数据库引擎的元数据子系统包含 XML 类型信息,例如 XML 架构集合和它们所包含的 XML 架构,以及基本 XSD 和关系类型系统之间的映射。几乎所有的 W3C XML 架构 1.0 规范(请参阅 http://www.w3.org/TR/2001/REC-xmlschema-1-20010502/ 和 http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/)都支持。XML 架构文档中的注释和注解都没有保留,并且不支持 key/keyref。

XML 架构集合

XML 架构集合是一个元数据实体(通过关系架构定义作用域),并且包含一个或多个相关(例如,使用<xs:import>) )或不相关的 XML 架构。XML 架构集合中单独的 XML 架构是使用其目标命名空间进行标识的。XML 架构集合是安全的实体,大致与表类似。

XML 架构集合是使用 CREATE XML SCHEMA COLLECTION 语义创建的,提供一个或多个 XML 架构。然后可以使用 XML 架构集合来类型化 XML 列。这种设计产生了灵活的数据模型,其中,按照不同的 XML 架构类型化的 XML 可以存储在相同的列中。当 XML 架构的数量很大时,这尤其方便。此外,这种设计还支持对 XML 架构进行一定程度的扩展。

另外,类型化的 XML 列中的选项 DOCUMENT 或 CONTENT 分别指定了 XML 树或片段是否可以存储在 XML 列中。默认为 CONTENT。对于 DOCUMENT,每个 XML 实例都必须指定其顶层元素的目标命名空间,该 XML 实例就是按照这个命名空间来进行有效性检查和类型化的。而对于 CONTENT,每个顶层元素都可以指定 XML 架构集合中的任何一个目标命名空间。该 XML 实例按照出现在实例中的所有目标命名空间进行有效性检查和类型化。

例:创建 XML 架构集合

假定要使用带有目标命名空间 http://myBooks 的 XML 架构来类型化 XML 实例。可以创建一个 XML 架构集合 myCollection 并将该 XML 架构作为 myCollection 的内容,如下所示:

CREATE XML SCHEMA COLLECTION myCollection AS 
'<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
  xmlns="http://myBooks" 
   elementFormDefault="qualified"
  targetNamespace="http://myBooks">
  <xsd:element name="bookstore" type="bookstoreType" />
  <xsd:complexType name="bookstoreType">
   <xsd:sequence maxOccurs="unbounded">
     <xsd:element name="book" type="bookType" />
   </xsd:sequence>
  </xsd:complexType>
  <xsd:complexType name="bookType">
   <xsd:sequence>
     <xsd:element name="title" type="xsd:string" />
     <xsd:element name="author" type="authorName" />
     <xsd:element name="price" type="xsd:decimal" />
   </xsd:sequence>
   <xsd:attribute name="genre" type="xsd:string" />
   <xsd:attribute name="publicationdate" type="xsd:string" />
   <xsd:attribute name="ISBN" type="xsd:string" />
  </xsd:complexType>
  <xsd:complexType name="authorName">
   <xsd:sequence>
     <xsd:element name="first-name" type="xsd:string" />
     <xsd:element name="last-name" type="xsd:string" />
   </xsd:sequence>
  </xsd:complexType>
</xsd:schema>'

同时也为该 XML 架构注册到的 myCollection 创建了一个新的元数据实体。

修改 XML 架构集合

ALTER XML SCHEMA COLLECTION 语句支持用新的顶层架构组件来扩展 XML 架构,并且还支持将新的 XML 架构注册到 XML 架构集合。下面的示例对此进行了演示。

例:改变 XML 架构集合

下面的语句显示了如何将具有目标命名空间 http://myDVD 的新 XML 架构添加到 XML 架构集合 myCollection 中:

ALTER XML SCHEMA COLLECTION myCollection ADD
'<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
  xmlns="http://myDVD" 
   elementFormDefault="qualified"
  targetNamespace="http://myDVD">
  <xsd:element name="dvdstore" type="dvdstoreType" />
  <xsd:complexType name="dvdstoreType">
   <xsd:sequence maxOccurs="unbounded">
     <xsd:element name="dvd" type="dvdType" />
   </xsd:sequence>
  </xsd:complexType>
  <xsd:complexType name="dvdType">
   <xsd:sequence>
     <xsd:element name="title" type="xsd:string" />
     <xsd:element name="price" type="xsd:decimal" />
   </xsd:sequence>
   <xsd:attribute name="genre" type="xsd:string" />
   <xsd:attribute name="releasedate" type="xsd:string" />
  </xsd:complexType>
</xsd:schema>'

XML 架构集合的目录视图

XML 架构集合的 SQL 目录视图允许用户重新构造单独的 XML 架构命名空间的内容。XML 架构集合在目录视图 sys.xml_schema_collections 中枚举。XML 架构集合“sys”由系统定义,并且包含一些预定义的命名空间,这些命名空间可用于所有用户定义的 XML 架构集合,而不必显式地加载它们。该列表包含用于 xml、xs、xsi、fn 和 xdt 的命名空间。

值得一提的其他两个目录视图是:sys.xml_schema_namespaces,它枚举每个 XML 架构集合中的所有命名空间;sys.xml_components,它枚举每个 XML 架构中的所有 XML 架构组件。

内置函数 XML_SCHEMA_NAMESPACE() 接受关系架构和 XML 架构集合的名称,以及 XML 架构的目标命名空间(可选)。它返回包含该 XML 架构的 XML 数据类型实例。假如目标命名空间参数被省去,那么该内置函数返回的 XML 实例就会包含 XML 架构集合中除预定义 XML 架构以外的所有 XML 架构。

例:枚举 XML 架构集合中的 XML 命名空间

将下列查询用于 XML 架构集合“myCollection”:

SELECT XSN.name
FROM    sys.xml_schema_collections XSC 
 JOIN sys.xml_schema_namespaces XSN ON 
(XSC.xml_collection_id = XSN.xml_collection_id)
WHERE    XSC.name = 'myCollection'   

例:从 XML 架构集合输出指定的 XML 架构

下面的语句从(关系)架构 dbo 中的 XML 架构集合“myCollection”输出带有目标命名空间 http://myBooks 的 XML 架构。

SELECT XML_SCHEMA_NAMESPACE (N'dbo', N'myCollection', 
N'http://myBooks')

对 XML 架构集合的访问控制

通过使用 SQL Server 2005 的安全模型,可以像任何 SQL 对象一样保护 XML 架构集合。您可以授予用户在数据库中创建 XML 架构集合的特权。每个 XML 架构集合都支持 ALTER、CONTROL、TAKE OWNERSHIP、REFERENCES、EXECUTE 和 VIEW DEFINITION 等权限。

执行 ALTER XML SCHEMA COLLECTION 语句需要 ALTER 权限。

假如要通过执行 ALTER AUTHORIZATION 语句将 XML 架构集合的所有权从一个用户转移到另一个用户,则需要 TAKE OWNERSHIP 权限。

REFERENCES 权限授权用户在任何需要架构绑定的地方使用 XML 架构,例如,类型化或约束 XML 列和参数。

要检查用户针对 XML 架构集合插入或更新的值的有效性,则需要 EXECUTE 权限。使用 XML 数据类型从类型化的 XML 列、变量和参数查询值也需要此权限。

VIEW DEFINITION 权限允许用户访问目录视图中与 XML 架构集合(所有的 XML 架构都包含其中)对应的行,所有 XML 架构组件都包含在这些 XML 架构中。

CONTROL 权限授予用户执行 XML 架构集合中任何操作的权限,包括使用 DROP XML SCHEMA COLLECTION 语句撤消 XML 架构集合。这意味着具有 XML 架构集合的其他权限。

除了表或 XML 列中的其他权限以外,还需要 XML 架构集合的权限。用户需要权限来创建表,并且还需要 XML 架构集合 C 中的 REFERENCES 权限来创建包含 XML 列 X(按照 C 类型化)的表 T。具有将数据插入列 X 的权限的用户可以这样做,前提是用户具有 XML 架构集合 C 中的 EXECUTE 权限。类似地,要使用 XML 数据类型方法来查询列 X 中的数据,用户需要列 X 中的 SELECT 权限和列 C 中的 EXECUTE 权限。然而,对于从列 X 检索整个 XML 值,列 X 中的 SELECT 权限就足够了,例如 SELECT X FROM T 或 SELECT * FROM T。

可以从用户撤回权限,还可以通过 SQL Server 2005 的安全模型拒绝用户的权限,正如允许其拥有权限一样。

目录视图的可视化

XML 架构集合中具有 ALTER、TAKE OWNERSHIP、REFERENCES、VIEW DEFINITION 或 CONTROL 权限的用户可以在目录视图中访问 XML 架构集合的行及其所包含的 XML 架构和它们的 XML 架构组件。该主体还可以通过使用内置函数 XML_SCHEMA_NAMESPACE() 来访问 XML 模式集合的内容。

假如用户的 VIEW DEFINITION 权限被拒绝,则该用户就不能在目录视图中或通过使用 XML_SCHEMA_NAMESPACE() 访问 XML 架构集合。

对 FOR XML 的增强

可以将 TYPE 指令生成的 XML 数据类型实例分配给 XML 列、变量或参数,或者使用 XML 数据类型方法对其进行查询。这使得可以嵌套 SELECT ...FOR XML TYPE 语句。

PATH 模式允许用户指定需要出现列值的 XML 树中的路径,并且与上述嵌套一起使用时更易于编写(与 FOR XML EXPLICIT 相比),不过,它也不可用于深层次。

与 ELEMENTS 一起使用的指令 XSINIL 可以将 NULL 映射为具有属性 xsi:nil="true" 的元素。新的 ROOT 指令允许在所有的 FOR XML 模式下指定根节点。新的 XMLSCHEMA 指令生成 XSD 内联架构。

要获得更多关于改进和示例的信息,请参阅由 Michael Rys 撰写的“What's New in FOR XML in Microsoft SQL Server”(MSDN 于 2004 年 6 月发布)。

性能指导原则

XML 数据模型比关系数据模型更加丰富,因而也更加复杂。XML 数据模型不仅允许对复杂的数据进行建模,而且还必须保留层次关系和数据中的文档顺序。文档顺序是通过基于 XML 节点标识符的排序维护的;这同时维护了层次关系。所有这些都对更复杂的查询计划产生了影响。

为了获得更好的性能,应该将结构化数据存储在表的关系列中。假如数据是半结构化或非结构化的,则需要选择使用 XML 数据模型来进行建模,并且加入 XML 标记,不过,别指望会有什么好的性能。XML 架构有助于查询优化。

SQL Server CLR 中的 XML 支持

通过使用 SQL Server CLR 支持,可以在托管代码中编写服务器端逻辑来执行业务规则。可以采用多种方法来将这些业务逻辑添加到 XML 数据中:

可以在 XML 值传入的托管代码中编写 SQLCLR 函数,并且使用 System.Xml 命名空间提供的 XML 处理功能。这样的一个例子是将 XSL 转换应用于 XML 数据,如下所述。另外,还可以将 XML 反序列化成一个或多个托管类,并且使用托管代码对它们进行操行。

可以编写 T-SQL 存储过程和函数来调用 XML 列中的处理,以满足您的业务需要。

例:应用 XSL 转换

考虑接受 XML 数据类型实例的 CLR 函数 TransformXml() 和存储在文件中的 XSL 转换,将该转换应用到 XML 数据,并返回已转换的 XML 结果。用 C# 编写的主干函数如下:

public static SqlXml TransformXml (SqlXml XmlData, string xslPath) {
   // Load XSL transformation
   XslTransform xform = new XslTransform();
   XPathDocument xslDoc = new XPathDocument (xslPath);
   xform.Load (xslDoc.CreateNavigator(),null);

   // Load XML data 
   XPathDocument xDoc = new XPathDocument (XmlData.CreateReader());
   XPathNavigator nav = xDoc.CreateNavigator ();

   // Return the transformed value
   SqlXml retSqlXml = new SqlXml (xform.Transform(nav, null));
   return (retSqlXml);
} 

一旦注册了程序集并创建了用户定义的 T-SQL 函数 SqlXslTransform()(与 TransformXml() 相对应),就可以从 T-SQL 调用该函数,如下面的查询所示:

SELECT SqlXslTransform (xCol, 'C:\yukon\xsltransform.xsl')
FROM    T
WHERE  xCol.exist('/book/title/text()[contains(.,"custom")]') =1

查询结果包含已转换的 XML 的行集。

SQLCLR 开辟了一个全新的世界,可用于将 XML 数据分解成表或属性提升,并且使用 System.Xml 命名空间中的托管类来查询 XML 数据。更多的信息可以在SQL Server 2005 和 Visual Studio 2005 在线书籍中找到。

四、SQL Server 2005 中的客户端 XML 处理

XML 数据类型的客户端支持

.NET 框架 V2.0 中的 ADO.NET XML 支持

SqlDataReader.GetSqlXml() 方法的 System.Data.SqlTypes 命名空间中,XML 数据类型是作为类 SqlXml 公开的。可以使用 SqlXml.CreateReader() 函数从 SqlXml 对象获得 XmlReader。

类型化 XML 列的 XML 架构集合的名称由三部分组成,可以从 XML 列的元数据中获得(通过使用 SqlDataReader 对象中的 GetSchemaTable() 或 GetSqlMetaData (int)),其三个部分作为三个属性,分别表示数据库 (XmlSchemaCollectionDatabase)、关系架构 (XmlSchemaCollectionOwingSchema) 和 XML 架构集合 (XmlSchemaCollectionName) 的名称。

客户端可以使用新的架构行集 XMLSCHEMA 从服务器检索 XML 架构。XMLSCHEMA 行集包含 XML 架构集合、目标命名空间和 XML 架构内容本身这样三列。

下面的示例显示了用于对 XML 数据类型进行托管访问的主干代码。

例:对 XML 数据类型进行进程内访问

下面的 C# 代码演示了如何从进程内提供程序访问 XML 数据类型。用于 SQL 客户端访问的代码需要适当地改变进程内访问。

using System;
using System.Xml;
using System.Data;
using System.Data.SqlTypes;
using System.Data.SqlServer;

class xmldtADONETReadAccessInProc
{
   static void ReadXmlDataType () {
      // in-proc connection to server
      SqlConnection conn = SqlContext.GetConnection();

      // prepare query to select xml data
      SqlCommand cmd = conn.CreateCommand();
      cmd.CommandText = "SELECT xCol FROM docs";

      // execute query and retrieve incoming data
      SqlDataReader r = cmd.ExecuteReader();
      r.Read();

      // access XML data type field in rowset
      SqlXml xml = r.GetSqlXml(0);
      new XmlTextWriter(Console.Out).WriteNode( 
xml.CreateReader(), true);
   }
}

例:使用 SQL 客户端提供程序更新 XML 数据类型列

下面的代码所显示的 WriteXmlDaraType() 方法使用 SQL 客户端程序提供程序来替换 XML 列中的值。用于进程内提供程序的代码是相似的。

using System;
using System.Xml;
using System.Data;
using System.Data.SqlTypes;
using System.Data.SqlClient;

class xmldtADONETUpdateAccess
{
   static void WriteXmlDataType () {
      // connection to server
      SqlConnection conn = new SqlConnection("server=server1;" +
" database=XMLtest; Integrated Security=SSPI");
      conn.Open();

   // update XML column at the server
   SqlCommand cmd = conn.CreateCommand();
   cmd.CommandText = "UPDATE docs SET xCol=@x WHERE id=1";

// set value of XML parameter
   SqlParameter p = cmd.Parameters.Add("@x", SqlDbType.Xml);
p.Value = new SqlXml(new XmlTextReader("<hello/>", 
XmlNodeType.Document, null));

// execute update and close connection
   cmd.ExecuteNonQuery();
conn.Close();
}
}

SQL 本机客户端访问

在新的 SQL 本机访问 (SQLNCLI) 的 OLE DB 提供程序中,可以通过 ISequentialStream 来将 XML 数据类型列作为 Unicode 字符串(DBTYPE_XML、DBTYPE_BSTR、DBTYPE_WSTR 和 DBTYPE_VARIANT)或 Unicode 字符串流进行检索。默认为 DBTYPE_XML。

由三部分组成的 XML 架构集合名称是在由 IDBSchemaRowset::GetRowset() 返回的 COLUMNS 架构行集的三个新列中携带的:SS_XML_CATALOGNAME 给出目录的名称;SS_XML_SCHEMANAME 给出 XML 架构集合所驻留的关系架构的名称;SS_XML_SCHEMACOLLECTIONNAME 给出 XML 架构集合的名称。这些名称都属于 DBTYPE_WSTR 类型。对于非类型化的 XML 列,这些列具有 NULL 值。

可以对 PROCEDURE_PARAMETERS 架构行集和 IColumnRowset:GetColumnRowset() 进行类似的改变。

要检索 XML 架构集合的内容,客户端可以通过在对 XML_SCHEMA_NAMESPACE() 的调用中使用这些名称来单独访问服务器,并且返回 XML 架构作为 XML 数据类型。另外,带有新的 SS_XMLSCHEMA 架构行集的 IDBSchemaRowset 可以返回目录的名称、关系架构的名称、XML 架构集合的名称、目标命名空间和 XML 架构。

要使用 SQLNCLI 进行 ODBC 访问,需要将 XML 数据类型映射到名为 SQL_SS_XML 的 Unicode 可变长度字符数据。由三部分组成的 XML 架构集合的名称是通过用于 XML 列的 SqlColAttribute 作为 CharacterAttributePtr 出现的。这些字段标识符为 SQL_DESC_SS_XML_SCHEMA_CATALOG_NAME、SQL_DESC_SS_XML_SCHEMA_SCHEMA_NAME 和 SQL_DESC_SS_XML_SCHEMA_COLLECTION_NAME,分别用于数据库、关系架构和 XML 架构集合的名称。

用户必须安装 SQL Server 2005 版的数据库服务器或客户端工具才能获取 SQL 本机客户端。在撰写本文时,还不能使用 MDAC 2.8 ADO 对 XML 数据类型进行 OLE DB 访问。

SQLXML — XML 和关系架构之间的映射

可以使用 SQLXML 映射技术来创建关系数据的逻辑 XML 视图。XML 视图(也称为“映射”或“带注解的架构”)是通过将特殊的注解添加到特定的 XSD 架构中创建的。于是,其他的 SQLXML 技术就可以使用这种带注解的架构,将针对逻辑 XML 视图进行的查询和更新转换成针对关系表进行的查询和更新:

当 XML 视图与 XPath 查询组合在一起时,SQLXML 将生成 FOR XML 查询来查找请求数据,并且根据架构的指定对其进行构形。

SQLXML Updategrams 表示对 XML 实例进行的更改,当与带注解的架构组合在一起时,将使用开放式并发将这些更改重新保存到关系更改中,以确保更新正确的数据。

SQLXML Bulkload 使用 XML 视图将 XML 数据“切碎”并存放到关系表中。

有关这些主题的更多信息,请访问 SQLXML documentation。

创建关系表的 XML 视图

要创建数据库的 XML 视图,需要从用于 XML 数据的 XSD 架构开始。数据库表/视图中的行将映射为该架构中复杂类型的元素。数据库中的列值映射为属性或简单类型的元素。

默认情况下,假如没有给出显式注解,SQLXML 就会假定复杂类型的元素将映射到表,而简单类型的元素和属性将映射到列。只有当元素和属性的名称与数据库中表和列的名称完全相同时,这才有效。

假如元素/属性的名称与它映射到的表/视图或列的名称不同,则必须创建显式映射。下面的注解用于指定 XML 文档中的元素或属性间的映射和数据库中的表(视图)或列之间的映射:

sql:relation — 将 XML 元素映射到数据库表。

sql:field — 将元素或属性映射到数据库列。

通过映射关系创建 XML 视图中的层次

在数据库中,表可以通过外键关系来关联。在 XML 中,这些相同的关系是通过嵌套的元素层次来表示的。为了在映射中构造正确的嵌套,必须指定关联元素的方式。可以使用 sql:relationship 注解在映射架构元素中建立这些关系。在这种注解中,可以指定父表和子表,以及每个表中需要用于执行加入的列。然后,SQLXML 将利用这些信息为映射构造正确的嵌套层次。

使用溢出存储未使用的数据

当 XML 数据有正规的结构时映射是有效的。然而,在 XML 中,可能有一些数据是非结构化的,还可能有一些数据没有映射到特定的列。要存储这种数据并随后检索它,可以使用 sql:overflow 注解。sql:overflow 注解指定存储所有未使用数据的列以及查询时从何处检索数据。

通过溢出列,还可以扩展 XML,而不必将其添加到数据库。可以在任何时候将元素和属性添加到 XML 结构,而不必在数据库中添加列来存储它们。可以将它们简单地存储到溢出字段,并且在适当的时候对其进行检索。

更多信息

有关创建 XML 视图和映射示例的详细信息,请参阅 Creating XML Views by Using Annotated XSD Schemas。

使用 XPath 来查询 XML 视图

一旦创建好数据库的 XML 视图,就可以使用 XPath 查询语言来查询视图,就好像它是实际的 XML 文档一样。SQLXML 支持 XPath 1.0 查询语言中的一个子集。当针对映射进行 XPath 查询时,SQLXML 将它们组合在一起,并且创建发送到 SQL Server 的 FOR XML EXPLICIT 语句。然后检索正确的数据,并根据映射对其进行构形。

请参阅 SQLXML 文档以获得关于 XML 视图中支持的 XPath 子集的详细信息。

使用 Updategrams 通过 XML 视图进行更新

可以通过 XML 视图修改(插入、更新或删除)SQL Server 中的数据库,方法是针对数据库的 XML 视图使用 Updategram。

updategram 的结构

updategram 是一个 XML 文档,带有<sync>、<before> 和<after> 元素,这些元素构成了 updategram 的语法。每个<sync> 块包含一个或多个<before> 和<after> 块。<before> 标识记录实例的现有状态(也称为“前状态”)。<after> 标识要更改的数据的新状态。updategram 是删除、插入还是更新记录实例取决于<before> 和<after> 块的内容。

插入操作

当记录实例出现在<after> 块而没有出现在相应的<before> 块中时,updategram 指示插入操作。在这种情况下,updategram 将<after> 块中的记录插入数据库。

删除操作

当记录实例出现在<before> 块而没有相应的记录出现在<after> 块中时,updategram 指示删除操作。在这种情况下,updategram 从数据库中删除<before> 块中的记录。

假如在 updategram 中指定的元素或者匹配表中的多行或者不匹配表中的任何行,则 updategram 会返回错误,并取消整个<sync> 块。updategram 中的元素一次只能删除一条记录。

更新操作

当更新现有的数据时,必须指定<before> 和<after> 块。updategram 使用<before> 块中指定的元素来标识数据库中的现有记录。<after> 块中相应的元素指示在执行更新操作之后记录的外观应该怎样。

<before>块中的元素必须只与数据库中的一个表行相匹配。假如该元素匹配多个表行或不匹配任何表行,则 updategram 都会返回错误,并且取消整个<sync> 块。

更多信息

要获得更多关于通过 XML 视图创建和使用 updategram 修改数据的信息,请参阅 Using Updategrams to Modify Data。

通过 XML 视图大量加载 XML 数据

XML Bulk Load 是一个 COM 对象,它允许您将 XML 数据加载到 SQL Server 表中。虽然通过使用 INSERT 语句和 OPENXML 函数可以将 XML 数据插入 SQL Server 数据库中,但是当需要插入大量的 XML 数据时,这种大量加载实用程序提供了更高的性能。XML Bulk Load 解释映射架构并标识 XML 数据要插入的表。然后,它将 XML 数据“切碎”并存放到关系表中。

在进行大量加载处理时,由于源 XML 文档可能很大,所以并不将整个文档读入内存。相反,XML Bulk Load 将 XML 数据解释为流,并且边解释它边读取它。当该实用程序读取数据时,它标识数据库表,从 XML 数据源生成适当的记录,然后将记录发送到 SQL Server,以供插入。

要获得更多关于 Bulk Load 如何工作和使用的信息,请参阅 Performing Bulk Load of XML Data。

SQLXML 数据访问方法

由于有了 SQL Server 2000,所以又增加了两种访问 SQLXML 功能的新方法:

SQLXML 托管类

SQLXML Web 服务

另外,还增强了对 SQL Server 的 HTTP 访问,以提供对模板中的 Updategram 的支持。

SQLXML 托管类

SQLXML 托管类公开 Microsoft .NET 框架内的 SQLXML 3.0 的功能。通过 SQLXML 托管类,可以编写 C# 应用程序从 SQL Server 的实例访问 XML 数据,将数据带入 .NET 框架环境,处理数据,并将更新重新发送回 SQL Server 以应用这些更新。

要获得更多关于如何使用 SQLXML 托管类的细节,请参阅 SQLXML .NET Support。

SQLXML Web 服务

SQLXML 中的 Web 服务支持将 SQL Server 作为 Web 服务公开,从而为客户端提供 SQL Server 功能。可以将 SOAP HTTP 请求发送到正在运行 SQLXML 的服务器,以执行存储过程、用户定义的函数 (UDF) 和模板。

通过使用所包括的用于 SQL Server 实用程序的 IIS 虚拟目录管理,也可以设置 Web 服务。一旦 Web 服务虚拟目录设置完毕,就可以将存储程序和模板添加到站点。然后,客户端就可以通过 HTTP 之上的 SOAP 协议来访问这些服务。

SQL Server 2005 现在包括对服务器中的 Web 服务的本机支持。然而,只有 SQLXML 支持通过 Web 服务访问 XML 模板。另外,可以在不同于数据库服务器的服务器上设置 SQLXML,创建远离数据库的单独中间层盒子。

要获得更多关于 SQLXML Web 服务的细节,请访问 Web Services (SOAP) Support in SQLXML。

.NET 框架 2.0 中的 XQuery

为了在 SQL Server 2005 中补充引入 XQuery,在 Visual Studio 2005 Beta 的 .NET 框架 V2.0 版中,System.Xml.Query 命名空间内置了一个中间层 XQuery 处理器。在这一部分中,我们说明该处理器如何提供查询和聚合完全不同的数据源的能力,例如文件和关系数据存储 SQL Server 数据库。

简单的说,下面的代码示例显示了一个查询,用于选择书店中所有 genre 属性值为 autobiography 的书,并且将这些书的标题作为一个新的书店写出。执行查询的称为“books.xml”的本地文件被加载,而将结果写入另一个称为“output.xml”的本地文件。

using (XmlWriter writer = XmlWriter.Create("output.xml"))
{
XQueryCommand xq = new XQueryCommand();
   string query =
         "<bookstore>" +
         "{ for $s in /bookstore/book " +
         "where $s/@genre='autobiography' " +
         "return $s/title }" +
         "</bookstore>";
   xq.Compile(query);
   xq.Execute("books.xml", new XmlUrlResolver(), writer);
}

查询表达式如下:

<bookstore>
{for $s in /bookstore/book
where $s/@genre='autobiography'
return $s/title}
</bookstore>

XQueryCommand 类是 XQuery 处理器。首先由 Compile 方法编译查询表达式,该方法对值进行类型检查,并且生成一个可执行文件。Execute 方法执行接受 books.xml 文档作为输入的查询。

在 SQL Server(具有将 T-SQL 语句内嵌到 XQuery 表达式的功能)中,XQueryCommand 类还能够对关系表中的数据进行查询。事实上,这使得能够将两个不同的查询语言之间的集成运用于不同的域,并且还补充了反向 SQL Server 2005,其中,T-SQL 语句内嵌到 XQuery 语句。下面的代码示例显示了一个查询,用于从样本 Northwind 数据库中选择所有的顾客。

declare namespace sql='http://schemas.microsoft.com/framework/2003/xml/sql';
<Customers>{
$for $customer in sql:query("select * from customers", "conn")/*
return customer
}</Customers>

sql:query 方法使您能够对连接进行 T-SQL 查询,其结果以与 XQuery doc() 函数完全相同的风格作为 XML 文档返回。假如结果是由关系类型组成的,则在客户端作为有效流转换成 XML。假如 SQL 查询的结果已经是 XML,例如 FOR XML 查询或 XML 数据类型的查询,则使用该查询的结果。假如没有返回结果,像在大多数 T-SQL DML 语句的情况下,这解释为空文档,它是有效的。来自此 XQuery 的输出如下所示,它列出了用 Customers 元素包装的顾客表中所有的顾客。在结果中自动为表中的每一行创建行元素。

<Customers>
  <row>
    <CustomerID>ALFKI</CustomerID>
    <CompanyName>Alfreds Futterkiste</CompanyName>
    <ContactName>Maria Anders</ContactName>
    <ContactTitle>Sales Representative</ContactTitle>
    <Address>Obere Str. 57</Address>
    <City>Berlin</City>
    <PostalCode>12209</PostalCode>
    <Country>Germany</Country>
    <Phone>030-0074321</Phone>
    <Fax>030-0076545</Fax>
  </row>
  <row>
    <CustomerID>ANATR</CustomerID>
    <CompanyName>Ana Trujillo Emparedados y helados</CompanyName>
    <ContactName>Ana Trujillo</ContactName>
    <ContactTitle>Owner</ContactTitle>
    <Address>Avda. de la Constitucion 2222</Address
    <City>Mexico D.F.</City
    <PostalCode>05021</PostalCode>
    <Country>Mexico</Country>
    <Phone>(5) 555-4729</Phone>
    <Fax>(5) 555-3745</Fax>
  </row>
...
</Customers>

为了通过 SQL Server 执行此查询,需要使用下面的代码。

static String ConnectionString = "database=Northwind;Data Source=localhost;Integrated Security=SSPI";

using (SqlConnection connection = new SqlConnection(ConnectionString))
{
       XmlDataSourceResolver resolver = new XmlDataSourceResolver();
   resolver.Add("conn", connection);
   XQueryCommand xq = new XQueryCommand();
   
StreamReader reader = new StreamReader("query.xml");
   xq.Compile(reader);
   
XmlWriterSettings settings = new XmlWriterSettings();
   settings.Indent = true;
   settings.ConformanceLevel = ConformanceLevel.Auto;
   using (XmlWriter writer = XmlWriter.Create("output.xml", settings))
   {
      try
      {
         connection.Open();
         xq.Execute(resolver, writer);
      }
      catch (Exception e)
      {
         Console.Out.WriteLine(e.Message);
         Console.Out.WriteLine(e.InnerException.Message);
      }
   }
}

这里,XmlDataSourceResolver 类提供使数据库连接与 sql:query 调用内可以使用的名称相关联的角色。首先从名为 query.xml 的本地文件中读取该查询,再进行编译,然后创建 XmlWriter 来写入结果。需要注意 XmlWriter 中使用的 ConformanceLevel.Auto 设置,它自动检测创建片段还是创建文档,这是必要的,因为可以借此构造结果中的元素,XQuery 能够生成这两者。在执行查询之前,向数据库打开连接,然后执行查询,并将结果写入文件。

sql:query 命令极其灵活而强大,它能够将有名称的参数提供给服务器上的 T-SQL 查询或调用的存储过程。sql:query 中命名的参数的作用完全等同于 System.Data.SqlClient 类。参数是在 T-SQL 查询中提供的,由后面紧跟名称的“@”表示。然后提供该名称和实际值之间的绑定作为查询的参数。对于每个命名的值,可以将相应的值(由序列中的位置决定)用于绑定。必须有与语言绑定数量相同的参数名称。下面的示例最好地对此进行了演示。

declare namespace sql='http://schemas.microsoft.com/framework/2003/xml/sql';
let $country := "USA"
<Customers>{
for $customer in sql:query("select * from customers where Country = @country",
"conn", {"@country"}, {$country})
return $customer/PostalCode
}</Customers>

这里,将参数 country 提供给 T-SQL 查询,从而将 $country XQuery 变量绑定到 @country 参数。结果是所有美国顾客的邮政编码列表。

sql:query 函数还可用于调用 T-SQL 存储过程(假如需要的话,就以上述方式传送参数)。下面的示例显示了如何调用 Northwind 数据库中的“Ten Most Expensive Products”存储过程,它没有输入参数。

declare namespace sql ='http://schemas.microsoft.com/framework/2003/xml/sql';
for $prod in sql:query('Exec "Ten Most Expensive Products"', "conn")
return $prod
小节

本文描述了 SQL Server 2005 中 XML 的补充技术。服务器端功能包括 XML 存储、索引和查询处理的本机实现。现有的功能(例如 FOR XML 和 OpenXML)已经得到增强。客户端支持包括增强 ADO.NET 来支持 XML 数据类型,以及在 System.Xml 中支持 XQuery 查询不同的 XML 源。另外,SQLXML 映射技术的 Web 版增强现在已经合并到 SQL Server 2005 中。

客户端和服务器端支持可用于不同的情况。XML 数据类型提供了一种简单的机制来存储 XML 数据,方法是将 XML 数据插入非类型化的 XML 列。使用 XML 架构定义类型化的 XML 有助于数据库引擎优化存储和查询处理,另外,还可以提供数据验证。

XML 数据类型保留了文档顺序,它对于像文档管理这样的应用程序非常有用。它还可以处理递归的 XML 架构。对于带有已知架构的结构化数据,关系数据模型仍然是最佳的选择。[n]varchar(max) 正好适合于查询和更新不重要的情况。

只要是在服务器上对存储在表中的关系数据使用以 XML 为中心的编程模型,就可以使用 SQLXML 映射技术。映射基于将 XML 架构定义为 XML 视图。映射可用于将 XML 数据大量加载到表以及使用 XPath 1.0 来查询表。SQLXML 映射技术不保留文档顺序,因此可用于 XML 数据处理而不是 XML 文档处理。

在即将发布的 .NET 框架 V2.0 Beta 1 版中,System.Xml 内的核心 XML 类支持读、写、操作和转换 XML。通过改进性能、可用性、类型化和查询,V2.0 版对 XML 的支持在创新性、标准支持和易用性方面继续保持行业领先。这包括用于操作 SQL Server 外的 XML 源的 XQuery 处理器在内。

服务器端技术和客户端技术互为补充。映射技术可以利用服务器端功能来增加其功能,例如维护文档顺序和递归架构,以及找到更多的应用领域。另一方面,CLR 将可扩展性和现有 XML 工具(如 XSLT 转换)的价值带入了 XML 数据类型。服务器和客户端中的 XQuery 实现彼此之间以及与 2003 年 11 月发布的 XQuery 规范草稿之间都是一致的。XML 架构实现在服务器端实现和中间层实现之间也是一致的。

Shankar Pal SQL Server 引擎的程序经理,主要研究服务器端 XML 技术。

Mark Fussell Microsoft WebData 组的首席程序经理,该小组负责开发 Microsof 的数据访问技术,其中包括 .NET 框架的 System.Xml System.Data 命名空间中的组件、Microsoft XML 核心服务 (MSXML) 以及 Microsoft 数据访问组件 (MDAC)。他的网络日记存放在http://weblogs.asp.net/mfussell

Irwin Dolobowsky 是 Webdata 组的程序经理。

一、简介

可扩展标记语言 (XML) 作为一种与平台无关的数据表示形式已被广泛采用。它对于在松散耦合且完全不同的系统,以及各种企业到企业 (B2B) 应用和工作流范畴内交换信息是很有用的。数据交换已成为 XML 技术的主要驱动力之一。.

XML 在企业应用程序中的使用正日益广泛,它主要用于对半结构化和非结构化数据进行建模。文档管理就是这样的一种应用程序。像电子邮件这样的文档是半结构化性质的。假如文档以 XML 的形式存储在数据库服务器中,就可以开发功能强大的应用程序来根据文档内容检索文档、查询部分内容(例如查找标题包含单词“背景”的部分),以及查询文档聚合。假如存在能够生成和使用 XML 的应用程序,则这样的方案就变得可行了。例如,Microsoft Office 2003 系统允许用户以 XML 标记的形式生成 Word、Excel、Visio 和 Infopath 文档。

为什么使用关系数据库来存储 XML 数据?

将 XML 数据存储在关系数据库中会给数据管理和查询处理带来好处。SQL Server 提供了强大的查询和修改关系数据的能力,而且已经扩展到查询和修改 XML 数据。这使得可以利用在过去的版本上所进行的投资,就如同基于成本的优化和数据存储领域一样。例如,关系数据库中的索引技术已经广为人知,而且已经扩展到用于索引 XML 数据,这样就可以使用基于成本的决策来优化查询。

XML 数据可以与现有的关系数据和 SQL 应用程序进行互操作,这样就可以在需要进行数据建模而又不破坏现有的应用程序的系统中引入 XML。数据库服务器还提供了管理功能来管理 XML 数据(例如备份、恢复和复制)。

这些功能促进了对 SQL Server 2005 中的原生 XML 支持的需求,从而解决了不断增加的 XML 使用的问题。SQL Server 2005 中的 XML 支持将给企业应用程序开发带来好处。

下面几部分将概述 SQL Server 2000 和 2005 中的 XML 支持,描述一些推动 XML 使用的方案,并且详细讨论服务器端和客户端的 XML 功能集。

SQL Server 2000 中的 XML 支持

这一部分简要概述了 SQL Server 2000 中的 XML 支持,以及随后发布的 SQLXML 客户端编程平台 Web 版,它提供了丰富的支持来将关系数据映射到 XML 数据或将 XML 数据映射到关系数据。

服务器端支持

在服务器上,XML 数据可以从表生成,并通过在 SELECT 语句中使用 FOR XML 子句来查询结果。这对于数据交换和 Web 服务应用程序是很理想的。FOR XML 的逆函数是一个名为 OpenXML 的关系行集合生成器函数;它通过求 XPath 1.0 表达式的值来从 XML 数据提取值,并将其放到行集合的列中。应用程序使用 OpenXML 来“切碎”传入 XML 数据,并将其存放到表中,或者用于通过 T-SQL 语言进行的查询。

客户端支持

SQL Server 2000 对客户端编程的支持称为 SQLXML。这项技术的核心是 XML 视图,它是 XML 架构和关系表之间的双向映射。SQL Server 2000 只支持 XDR 架构的映射,尽管在后续的 Web 版中增加了对 XSD 的支持。XML 视图允许使用 XPath 1.0 的一个子集来进行查询,其中,可以使用映射将路径表达式转换成底层表中的 SQL 查询,并且将查询结果打包成 XML 结果。

SQLXML 还支持您创建 XML 模板,这使得您可以创建带有动态部分的 XML 文档。在 XML 文档中,您可以嵌入 FOR XML 查询和/或映射查询之上的 XPath 1.0 表达式。在执行 XML 模板时,可以用查询的结果来替换查询块。通过这种方式,您可以创建带有某些静态内容和某些数据驱动的动态内容的 XML 文档。

在SQL Server 2000 中,有两种访问 SQLXML 功能的主要方法:

SQLXMLOLEDB Provider。SQLXMLOLEDB Provider 是一个 OLE DB 提供程序,它通过 ADO 公开 Microsoft SQLXML 功能。

HTTP 访问。SQL Server 2000 中的 SQLXML 功能也可以使用 SQLXML ISAPI 过滤器通过 HTTP 进行访问。通过使用我们的配置工具,您可以建立网站来检索传入请求,从而通过 HTTP 执行 XML 模板、XML 视图之上的 FOR XML 和 XPath 1.0 语句。

XML 支持的局限性

服务器和客户端编程平台为基于表格和 XML 数据之间的映射生成和使用 XML 数据提供了丰富的支持。这能够相当好地处理结构化 XML 数据。在 SQLXML 中,查询语言是 XPath 1.0 的一个子集,并且有一些局限性。例如,不支持 descendant-or-self 轴 (//)。因此,在开发某些解决方案时会存在一定的限制。例如,不保存 XML 文档顺序,而这对于像文档管理这样的应用程序来说是非常关键的。此外,还不支持递归的 XML 架构。尽管存在这样一些局限性,但是客户端 SQLXML 和服务器 XML 功能还是在应用程序开发中得到了广泛的使用。SQL Server 2005 解决了许多这样的限制,扩展了关系 XML 交换功能,并且还提供了原生 XML 支持。

SQL Server 2005 中的 XML 支持概述

这一部分简要概述了 SQL Server 2005 中增加的新的 XML 支持,它是通过.NET 框架 V2.0 中的支持和本机客户端数据访问(如 OLE DB)进行补充的。

XML 数据类型

XML 数据模型具有一些特性,这些特性使得映射到关系数据模型非常困难,假如不是完全不可能的话。XML 数据具有可以递归的层次结构;关系数据库提供对层次数据(建模为外键关系)的弱支持。文档顺序是 XML 实例的固有属性,并且必须保存在查询结果中。这与关系数据形成了对比,关系数据是无序的,必须通过附加的排序列来强制进行排序。在查询时重新组合结果是很费力的,因为实际的 XML 架构将 XML 数据分解到大量的表中。

SQL Server 2005 引入了一种称为 XML 的本机数据类型。用户可以创建这样的表,它在关系列之外还有一个或多个 XML 类型的列;此外,还允许带有变量和参数。为了更好地支持 XML 模型特征(例如文档顺序和递归结构),XML 值以内部格式存储为大型二进制对象 (BLOB)。

SQL Server 2005 提供了 XML 架构集合,可以将其作为一种方法来以元数据的形式管理 W3C XML 架构。XML 数据类型可以与 XML 架构集合相关联,以便对 XML 实例强加架构限制。当 XML 数据与 XML 架构集合相关联时,它称为类型化的 XML;否则,就称为非类型化的 XML。在一个框架中可以同时容纳类型化的 XML 和非类型化的 XML,保留 XML 数据模型,并且强制采用 XML 语义进行查询处理。底层关系基础结构被广泛用于这一目的。它支持关系数据和 XML 数据之间的互操作,这为更广泛地采用 XML 功能开辟了道路。

XML 数据类型查询和数据修改

可以使用 T-SQL SELECT 语句来检索 XML 实例。在 XML 数据类型中提供了五种内置的方法来查询和修改 XML 实例。

XML 数据类型的方法接受 XQuery,它是一种新出现的 W3C 标准语言(目前处于 Last Call(最后请求)状态),并且包括导航语言 XPath 2.0。也可以使用一种语言来修改 XML 数据,比如添加或删除子树和更新标量值。与一大组函数一起,嵌入式的 XQuery 和数据修改语言为处理 XML 数据提供了丰富的支持。

XQuery 类型系统与 W3C XML 架构类型是一致的。大多数 SQL 类型与 XQuery 类型系统是兼容的(例如,小数)。少数类型(例如,xs:duration)是以内部格式存储的,并且可以通过适当的解释来与 XQuery 类型系统兼容。

编译阶段检查 XQuery 表达式和数据修改语句的静态类型的正确性,并且在类型化 XML 的情况下使用 XML 架构来进行类型推理。假如表达式由于类型安全冲突而在运行时失败,则会产生静态类型错误。

XML 索引

查询执行在运行时处理每个 XML 实例;假如 XML 值比较大或需要在表的许多行中对查询进行求值,则查询执行就会变得非常费时。因此提供了一种索引 XML 列的机制来加速查询。

B+ 树广泛用于建立关系数据的索引。XML 列的主 XML 索引在该列中 XML 实例的所有标记、值和路径上都创建一个 B+ 树索引。通过这种方式,可以有效地对 XML 数据中的查询进行求值,并且在保留文档顺序和文档结构的同时从 B+ 树重新组合 XML 结果。

可以在 XML 列中创建次 XML 索引来加速不同类别的常见查询:PATH 索引(用于基于路径的查询)、PROPERTY 索引(用于属性包的情况)和 VALUE 索引(用于基于值的查询)。

XML 架构处理

XML 列、变量和参数可以根据 XML 架构的集合(它可能与另一个集合相关(例如,通过使用 )或不相关)有选择地进行类型化。每个类型化的 XML 实例都从它遵循的 XML 架构集合指定目标命名空间。数据库引擎在数据分配和修改时根据 XML 架构来验证实例的有效性。

XML 架构信息用于存储和查询优化。类型化的 XML 实例在内部的二进制表示中包含类型化的值,这与 XML 索引中的一样。通过这种方式,可以有效地处理类型化 XML 数据。

关系数据和 XML 数据的集成

用户可以在同一个数据库中存储关系数据和 XML 数据。简而言之,数据库引擎除了知道如何遵循关系数据模型之外,还知道如何遵循 XML 数据模型。在升级到 SQL Server 2005 之后,关系数据和 SQL 应用程序仍然能够正常工作。在服务器上,可以将驻留在文件和文本或图像列中的 XML 数据移到 XML 数据类型的列中。通过使用 XML 数据类型的方法,可以建立 XML 列的索引,并对其进行查询和修改。

数据库利用现有的关系基础结构和引擎组件(例如存储引擎和查询处理器)来进行 XML 处理。例如,XML 索引可以创建 B+ 树,而且可以在 Showplan 输出中查看查询计划。通过集成到关系框架中,数据管理功能(如备份/恢复和复制)可用于 XML 数据。同样地,新的数据管理功能(如数据库镜像和快照隔离)可以处理 XML 数据类型,以提供无缝的用户体验。

结构化数据应该存储在表和关系列中。当应用程序需要执行细粒度查询和数据修改时,对于使用 XML 的半结构化数据和标记数据,XML 数据类型是比较合适的选择。

FOR XML 和 OpenXML 增强

现有的 FOR XML 功能已经在几个方面得到了增强。它能够处理 XML 数据类型的实例和其他新的 SQL 类型,例如 [n]varchar(max)。

新的 TYPE 指令生成的 XML 数据类型实例可以分配给 XML 列、变量或参数,也可以使用 XML 数据类型的方法进行查询。这允许嵌套 SELECT ...FOR XML TYPE 语句。

PATH 模式允许用户指定出现列值的 XML 树中的路径,并且与上述嵌套一起使用时比 FOR XML EXPLICIT 更易于编写。

XSINIL 指令与 ELEMENTS 一起使用,可以将 NULL 映射到带有属性 xsi:nil="true" 的元素。另外,新的 ROOT 指令还允许在所有的 FOR XML 模式下指定根节点。新的 XMLSCHEMA 指令生成 XSD 内联架构。

OpenXML 的功能增强包括在 sp_preparedocument 中接受 XML 数据类型以及在行集中生成 XML 和新的 SQL 类型的列。

对 XML 数据类型的客户端访问

客户端可以通过几种方式访问服务器中的 XML 数据。使用 ODBC 和 OLE DB 的本机 SQL 客户端访问以 Unicode 字符串的形式传递 XML 数据类型。OLE DB 还提供对流 Unicode 数据的 XML 数据类型的 ISequentialStream 访问。

托管访问通过 .NET 框架 V2.0 中的 ADO.NET 将 XML 数据作为一个名为 SqlXml 的新类进行传递。它支持一个名为 CreateReader() 的方法,该方法返回 XmlReader 实例来读取返回的 XML。同样地,数据集能够将 XML 数据类型的实例加载到中间层的列,中间层可以作为 XML 文档进行编辑,并且重新保存到 SQL Server。这两者都支持对服务器发出 SQL 查询,以检索在中间层操作的 XML 列。

在 SQL Server 2005 中,可以使用直接对 HTTP 终结点进行的SOAP 访问来查询、检索和修改 XML 数据。

本机和托管客户端技术都提供了新的接口来检索类型化 XML 列的 XML 架构集合。

带有 XQuery 的客户端 XML 支持

除了使用 ADO.NET 来检索表并将结果存放到关系数据集中之外,您还可以使用 XQueryCommand 类将关系数据直接加载到中间层的 XML 文档。该类提供了一个中间层 XQuery 处理器,它能够将 T-SQL 语句作为 XQuery 语言的一部分嵌入。通过这种方式,您可以查询本地 XML 文件,也可以从 SQL Server 检索数据(直接从表或者通过数据库存储程序),再将数据构造成 XML 格式,然后将其加载到中间层 XML 文档。这大大简化了 SQL Server 中的查询,将查询结果构造成特定的 XML 格式,并使其流向业务合作伙伴,而不需要将数据加载到数据集来执行数据转换。

二、推动 XML 存储方案

XML 数据变得越来越普遍.它可以表示客户数据,带有或不带有描述数据的 XML 架构均可。XML 数据和 XML 架构需要在一起进行管理。实际应用程序的 XML 架构通常比较复杂,因而将这样的 XML 架构映射到表和列是一项复杂的任务。当 XML 架构改变或新的架构加入系统时,长时间维护这样的映射是一件很麻烦的事情。通常,XML 数据存储在文件系统或数据库服务器的文本列中。文本列有数据管理方面的好处(例如,复制和备份/恢复),但是不提供任何基于数据的 XML 结构的查询支持。在具有原生 XML 支持的情况下,使用 XML 进行应用程序开发变得更快。

自定义属性管理

一些应用程序(例如用户界面软件)允许用户从一组固定的属性中进行选择。而其他应用程序则允许用户定义他们自己感兴趣的属性。假如这样的自定义属性是以 XML 的格式存储的,它们就可以很好地进行管理。应用程序可以支持标量属性以外的属性:

可以支持对象中的多值属性,例如,多个电话号码。

可以支持复杂属性,例如,一个文档的作者属性可能是该作者的联系信息。

可以将对象属性存储在 XML 数据类型的列中并建立索引,从而提高查询处理的效率。

数据交换和工作流

XML 允许采用平台无关的方式在应用程序之间交换数据。可以使用 XML 标记将这样的数据建模为消息。代替不断地分割和生成 XML 消息,以 XML 的格式存储消息是明智的。这正是数据流所需要的。到达工作流阶段的 XML 消息携带着当前的状态。每个消息都需要进行处理,处理的进展记录在 XML 内容中(如状态改变),然后将 XML 数据转发到工作流处理的下一个阶段。消息可能是不同类型的,甚至可能是半结构化的,并且有不同的 XML 架构与它们相关联,因此将它们映射到表并不总是一件轻而易举的事。

针对不同垂直领域(如金融数据和地理空间数据)的基于 XML 的标准正在形成。这些标准根据可以查询和更新的实例数据来描述数据的结构。通常,实际数据采用的是二进制形式,而 XML 数据提供关于它们的元数据信息。

举一个简单的例子,为了将一个输入参数表传送到存储程序或函数,应用程序首先将数据转换成 XML,然后将其作为一个 XML 数据类型参数进行传递。在存储过程或函数内,从 XML 参数重新生成行集。

文档管理

假设有一个呼叫中心,它采用 XML 文档来维护患者记录和谈话。当患者发起呼叫时,呼叫中心希望恢复前面的谈话以设置传入呼叫的情景。这可以通过查询 XML 标记实现,从而给应用程序带来好处。此外,还可以方便地检索以前谈话的细节并记录当前的谈话。

像电子邮件之类的文档本质上是半结构化的。带有 XML 标记的文档正变得越来越容易创建,例如,使用 Office 2003。可以将这些 XML 文档存储在 XML 列中,并建立索引,进行查询和更新。因此,通过利用原生 XML 支持,开发人员可以做更多的事情。

三、SQL Server 2005 中的服务器端 XML 处理

SQL Server 2005 支持包括提供一个数据库,在这个数据库中,您可以存储关系数据和 XML 数据。

XML 数据类型

您可以使用普通的 CREATE TABLE 语句来创建带有 XML 列的表。然后就可以采用一种特别的方式来建立 XML 的索引。

非类型化的 XML

SQL Server 2005 XML 数据类型实现了 ISO SQL-2003 标准 XML 数据类型。因此,它不仅可以存储格式良好的 XML 1.0 文档,而且可以存储所谓的 XML 内容片段(带有文本节点和任意数目的顶层元素)。在对数据进行格式良好性检查时,并不要求将 XML 数据类型绑定到 XML 架构,但是格式不规范的数据将被拒绝。

当架构不是已知先验的 并且因此而导致基于映射的解决方案不可能实现时,就可以使用非类型化的 XML。假如架构是已知的,但映射到关系数据模型非常复杂并且难于维护,或者存在多个架构而且这些架构是后来根据外部要求绑定到数据的,也可以使用非类型化的 XML。

例:表中非类型化的 XML 列

下面的语句创建一个名为“docs”的表,该表带有整型主键“pk”和非类型化的 XML 列“xCol”:

CREATE TABLE docs (pk INT PRIMARY KEY, xCol XML not null)

也可以创建一个包含多个 XML 列或关系列、带主键或不带主键的表。

类型化的 XML

假如 XML 架构集合中有描述 XML 数据的 XML 架构,就可以将 XML 架构集合与产生类型化 XML 的 XML 列相关联。可以使用 XML 架构来验证数据的有效性,在编译查询和数据修改语句时执行比非类型化的 XML 更精确的类型检查,以及优化存储和查询处理。

类型化的 XML 列、参数和变量可以存储 XML 文档或内容,可以在声明时将其指定为一个选项(分别为DOCUMENT 或 CONTENT,默认为 CONTENT)。此外,还必须提供 XML 架构集合。假如每个 XML 实例都正好有一个顶层元素,则指定 DOCUMENT;否则,使用 CONTENT。查询编译器在类型检查中使用 DOCUMENT 标记来推理 singleton 顶层元素。

例:表中的类型化 XML 列

XML 列、变量和参数可以绑定到一个 XML 架构集合(请参阅本文后面的“XML 架构处理”一节以获得更多的详细信息和示例)。假定 myCollection 代表这样一个集合。下面的语句创建一个表 XmlCatalog,带有使用 myCollection 进行类型化的 XML 列文档。类型化的 XML 列还被指定为接受 XML 片段,而不只是 XML 文档。

CREATE TABLE XmlCatalog ( 
   ID INT PRIMARY KEY, 
   Document XML(CONTENT myCollection))

约束 XML 数据类型的列

除了类型化一个 XML 列之外,还可以在类型化和非类型化的 XML 数据类型的列中使用关系(列或行)约束。大部分 SQL 约束同样可应用于 XML 列,值得注意的唯一例外是主键和外键约束,因为 XML 数据类型的实例是不兼容的。因此,可以指定 XML 列可为空或不可为空,提供默认值,并且在列中定义 CHECK 约束。例如,非类型化的 XML 列可以有 CHECK 约束来验证存储的 XML 实例是否符合 XML 架构。

在下列条件下使用约束:

业务规则不能用 XML 架构表示。例如,花店的交付地址必须在其业务场所 50 英里的范围内,这可以编写成 XML 列中的一个约束条件。该约束条件可以包括 XML 数据类型的方法。

约束条件涉及表中其他的 XML 或 非 XML 列。这样的一个例子就是,使 XML 实例中存在的 Customer (/Customer/@CustId) 的id 与整型 CustomerID 列中的值相匹配。

例:约束 XML 列

要确定<book>的<author>的<last-name> 不同于<author>的<first-name> ,可以指定下列 CHECK 约束:

CREATE TABLE docs (pk INT PRIMARY KEY, xCol XML not nullCONSTRAINT CK_name CHECK (xCol.exist('/book/author[first-name = last-name]') = 0))

文本编码

SQL Server 2005 将 XML 数据存储为 Unicode (UTF-16)。从服务器检索的XML 数据,其结果也是 UTF-16 编码的。假如想要采用一种不同的编码方式,就需要在检索数据之后进行必要的转换,转换的方法有两种,一种是通过强制类型转换,另一种是在中间层执行转换。例如,可以在服务器上将 XML 数据强制转换成 varchar 类型,在这种情况下,数据库引擎会通过 varchar 类型的排序所确定的编码方式来序列化 XML。

存储 XML 数据

可以通过多种方式为 XML 列、参数或变量提供 XML 值。

作为隐式转换到 XML 数据类型的字符或二进制 SQL 类型。

作为文件的内容。

作为 XML 发布机制 FOR XML 的输出(带有生成 XML 数据类型实例的 TYPE 指令)

对提供的值进行格式良好性检查,并且允许存储 XML 文档和 XML 片段。假如数据没有通过格式良好性检查,则拒绝它,并发出一个适当的错误消息。

对于类型化的 XML,需要检查提供的值是否符合已注册到类型化 XML 列的 XML 架构集合的 XML 架构。假如该 XML 实例没有通过这种有效性验证,则拒绝它。此外,仅当 CONTENT 允许提供 XML 文档和内容时,类型化的 XML 中的 DOCUMENT 标记才将所接受的值限制为 XML 文档。

例:将数据插入非类型化的 XML 列

下列语句在表 docs 中新插入一行,其中在整型的 pk 列插入的值为 1,而在 XML 列插入的是 实例。 数据(作为字符串提供)被隐式地转换为 XML 数据类型,并且在插入的过程中进行格式良好性检查。

INSERT INTO docs VALUES (1, 
'<book genre="security" publicationdate="2002" ISBN="0-7356-1588-2">
   <title>Writing Secure Code</title>
   <author>
      <first-name>Michael</first-name>
      <last-name>Howard</last-name>
   </author>
   <author>
      <first-name>David</first-name>
      <last-name>LeBlanc</last-name>
   </author>
   <price>39.99</price>
</book>')

例:将来自文件的数据插入非类型化的 XML 列

如下所示的 INSERT 语句使用 OPENROWSET 读取文件 C:\temp\xmlfile.xml 的内容作为 BLOB。在表 docs 中新插入一行,值 10 作为主键,而 BLOB 作为 XML 列 xCol。格式良好性检查出现在文件内容分配到 XML 列时。

INSERT INTO docs 
SELECT 10, xCol
FROM    (SELECT * FROM OPENROWSET 
      (BULK 'C:\temp\xmlfile.xml',
      SINGLE_BLOB) AS xCol) AS R(xCol)

例:将数据插入类型化的 XML 列

类型化的 XML 列需要 XML 实例数据指定用于对其进行类型化的 XML 架构的目标命名空间(该命名空间可以为空)。在下面的示例中,这是通过命名空间声明 xmlns=http://myDVD 来实现的。

INSERT XmlCatalog VALUES(2, 
'< xml version="1.0" >
<dvdstore xmlns="http://myDVD">
  <dvd genre="Comedy" releasedate="2003">
    <title>My Big Fat Greek Wedding</title>
    <price>19.99</price>
  </dvd>
</dvdstore>')

例:存储使用带有 TYPE 指令的 FOR XML 生成的 XML 数据

通过 TYPE 指令增强的 FOR XML 可以生成像 XML 数据类型实例这样的结果。结果 XML 可以分配到 XML 列、变量或参数。在下面的语句中,使用 FOR XML TYPE 生成的 XML 实例被分配给 XML 数据类型的变量 @xVar。可以使用 XML 数据类型的方法来查询变量。

DECLARE @xVar XML
SET     @xVar = (SELECT * FROM docs FOR XML AUTO,TYPE)

存储表示

XML 数据类型的实例存储在内部的二进制表示中,该表示是可流化的,并且经过了优化,从而可以更有效地进行解析。标记映射到整型值,而映射的值存储在内部表示中。这也产生了一些数据的压缩。

对于非类型化的 XML,节点值存储为 Unicode (UTF-16) 字符串,因此需要进行运行时类型转换才能执行操作。例如,为了求谓词 /book/price > 9.99 的值,必须将该书的价格值转换为小数。而对于类型化的 XML,值的编码类型为在 XML 架构中指定的类型。这使得数据的解析更加有效,并且还不必进行运行时转换。

存储的二进制形式被限制为每 XML 实例 2 GB,这可以适应大部分的 XML 数据。此外,XML 层次的深度还被限制为 128 层。

XML 数据的信息集内容被保留。但是,它不可能是与文本 XML 一模一样的副本,因为下列信息没有保留:无关紧要的空白、属性的顺序、命名空间前缀和 XML 声明。

数据建模考虑事项

通常,关系数据类型和 XML 数据类型的列的组合比较适合数据建模。可以将 XML 数据中的一些值存储在关系列中,而将其余的值或全部的 XML 值存储在 XML 列中。这可以产生更好的性能和锁定特性。

对于 singleton 值(即单值属性),XML 数据中的值可以提升为同一个表中的计算列。多值属性需要单独的属性表,该表必须使用触发器进行填充和维护。查询需要直接针对属性表进行编写。

对于锁定和更新特性,存储在 XML 列中的 XML 数据的粒度是至关重要的。SQL Server 将相同的锁定机制用于 XML 和非 XML 数据。假如粒度比较大,则在多用户的情况下,为了更新而锁定大的 XML 实例会引起吞吐量的下降。而另一方面,严格的分解会失去对象的封装性并增加重新组合的成本。

查询和修改 XML 数据

查询存储在 XML 列中的 XML 实例需要解析列中的二进制 XML 数据。与解析文本形式的 XML 数据相比,解析二进制的 XML 要快得多。XML 索引避免了重新解析,这将在“建立 XML 数据的索引”一节中进行讨论。

XML 数据类型中的方法

假如感兴趣,可以检索全部 XML 值,也可以检索部分 XML 实例。这可以使用四个 XML 数据类型的方法来实现:query()value()exist()nodes(),它们接受 XQuery 表达式作为参数。第五个方法 modify() 允许修改 XML 数据并接受 XML 数据修改语句作为输入。

query() 方法用于提取 XML 实例的部分。XQuery 表达式求值为一个 XML 节点列表。以这些节点中的每一个为根的子树按照文档顺序返回。结果类型为非类型化的 XML。

value() 方法从 XML 实例提取标量值。它返回 XQuery 表达式所求值的节点的值。该值被转换为 value() 方法的第二个参数所指定的 T-SQL 类型。

exist() 方法用于对 XML 实例进行存在性检查。假如 XQuery 表达式求值为非空节点列表,则返回 1;否则,返回 0。

nodes() 方法产生特定 XML 数据类型的实例,每个实例都将其上下文设置为 XQuery 表达式所求值的不同节点。特定的 XML 数据类型支持 query()value()nodes()exist() 方法,并且可以用于 count(*) 聚合和 NULL 检查。所有其他的使用都会产生错误。

modify() 方法允许修改 XML 实例的某些部分,例如添加或删除子树,或者更新标量值(如将书的价格从 9.99 替换为 39.99)。

例:使用 query() 方法

考虑下面的表 docs 的 XML 列 xCol 中的查询,它提取 元素下的任何位置的 id 为 123 的 元素。该查询也从整型主键列检索值。SELECT 列表中的 query() 方法会为生成 元素序列的表中的每一行进行求值, 元素及其子树是按照文档顺序进行检索的。对于每个 XML 实例,假如没有 id 为 123 的 元素或者其下没有 元素,则不会返回结果,也就是说,query() 方法的返回值为 NULL。

SELECT pk, xCol.query('/doc[@id = 123]//section')   
FROM   docs

NULL 返回值可以在外部 SELECT 语句中过滤掉。或者也可以使用 exist() 方法,如下一个示例所示。

例:使用 exist() 方法

考虑下面的查询,它在表 docs 的 XML 列 xCol 中包括 query()exist() 方法。exist() 方法求路径表达式 /doc[@id = 123] 的值,检查是否存在顶层 元素,该元素具有一个称为 id 的属性,且其值为 123。对于每个这样的行,SELECT 子句中的 query() 方法都会进行求值;在这个示例中,query() 方法在 元素下的任何位置都产生一个 元素序列。从 exist() 方法返回 0 的任何行都会被忽略。

SELECT xCol.query('/doc[@id = 123]//section')   
FROM   docs
WHERE  xCol.exist ('/doc[@id = 123]') = 1

例:使用 value() 方法

下面的查询使用 value() 方法以 Unicode 字符串的形式提取文档第三部分的标题。结果的 SQL 类型的 nvarchar(max) 被指定为 value() 方法的第二个参数。XQuery 函数 data() 从 节点提取标量值。

SELECT xCol.value( 'data((/doc//section[@num = 3]/heading)[1])', 'nvarchar(max)') FROM docs

XQuery 语言

众多的 XML 都来源于存储在文件系统、Web 服务或配置文件中的 Office 文档。事实上,以 XML 格式或作为虚拟 XML 文档生成的数据正在不断地增加。为了处理这些数量越来越多的数据,一种强大的查询语言 XQuery 应运而生。在 XQuery 语言规范(位于 http://www.w3.org/TR/xquery)中将选择 XQuery 的理由描述为:

一种巧妙地使用 XML 结构的查询语言,可以跨各种数据表示查询,而不管这些数据是物理存储在 XML 中,还是通过中间件被视为 XML。该规范描述了一种称为 XQuery 的查询语言,它旨在能广泛应用于许多类型的 XML 数据源。

XQuery 旨在满足 W3C XML 查询工作组 XML 查询 1.0 要求和 XML 查询用例中的用例所确定的要求。它是一种旨在使查询简洁易懂的语言。它还相当地灵活,可以查询大范围的 XML 信息源,其中包括数据库和文档。

XQuery 还可以概括为如下表述:XQuery 语言之于 XML 正如 SQL 语言之于关系数据库。

内嵌于 T-SQL 的 Xquery 子集(位于 http://www.w3.org/TR/xquery/)是一种支持查询 XML 数据类型的语言。这种语言正在由 Worldwide Web Consortium (W3C) 进行开发(目前处于最后请求状态),所有主要的数据库厂商(包括 Microsoft 在内)都参与其中。我们的实现与 2003 年 11 月发布的 XQuery 草案是一致的。

XQuery 将 XPath 2.0 作为导航语言包括在内。SQL Server 2005 的 XQuery 实现提供了用于遍历节点 (for)、节点检查 (where)、返回值 (return) 和排序 (order by) 的构造。它也提供了用于在查询过程中重新进行数据构形的元素构造。

SQL Server 2005 还提供了用于 XML 数据类型的数据修改 (DML) 的语言构造(请参阅下面的“数据修改”一节以获得更多信息)。下面的示例演示了如何将 XQuery 用于 XML 数据类型。

例:使用 XQuery 中丰富的语言构造

下面的查询显示了如何一起使用几个 XQuery 语言构造。它从 id 为 123 的文档返回区域号为 3 和更高的区域中的标题,并将其包装在新标记 中。

SELECT pk, xCol.query('
   for $s in /doc[@id = 123]//section
   where $s/@num >= 3
   return <topic>{data($s/heading)}</topic>')   
FROM docs

“for”遍历 id 为 123 的<doc> 元素下的所有<section> 元素,并且将每个这样的<section> 元素绑定到变量 $s。“where”确保区域号(<section> 元素的 @num 属性)为 3 或更高。该查询按照文档顺序返回区域<heading> 中的值,并且将其包装在一个称为<topic> 的构造元素中。

查询编译和执行

SQL 语句是由 SQL 解析器解析的。当它遇到 XQuery 表达式时,它就会跳转到 XQuery 编译器,然后编译 XQuery 表达式。这会产生一个查询树,并将其嫁接到整个查询的查询树上。

整个查询树会进行查询优化并产生物理查询计划,该计划是根据基于成本的评估得出的。Showplan 输出显示了大部分关系运算符和一些新的运算符(例如,用于 XML 处理的 UDX)。

查询执行与关系框架中的其余部分一样,都是面向元组的。在表 docs 的每一行上都求 WHERE 子句的值;这需要在运行时解析 XML BLOB 以求出 XML 数据类型方法的值。假如条件满足,则锁定行,然后在行中求 SELECT 子句的值。结果以 XML 数据类型的形式产生(用于 query() 方法),并且转换成指定的目标类型(用于 value() 方法)。

而假如该行不满足 WHERE 子句中的条件,它就会被忽略,执行转到下一行。

XML 数据修改

SQL Server 2005 提供了用于数据修改的构造作为对 XQuery 的一个扩展。子树可以在指定的节点之前或之后插入,或者作为最左边或最右边的子节点插入。此外,子树也可以插入到父节点,在这种情况下,它成为父节点最右边的子节点。属性、元素和文本节点插入都支持。

支持删除子树。在这种情况下,整个子树就从 XML 实例中被移除。

标量值可以用新的标量值进行替换。

例:将子树插入 XML 实例

这个示例显示了 modify() 方法的使用,它将一个新的<section> 元素插入编号为 1 的 元素的右边。

UPDATE docs SET xCol.modify('
  insert 
   <section num="2">
         <heading>Background</heading>
</section>                
  after (/doc/section[@num=1])[1]')

例:将这本书的价格更新为 $49.99

下面的更新语句将 ISBN 为 1-8610-0311-0 的书的<price> 替换为 $49.99。该 XML 实例是通过 XML 架构 http://myBooksinstance 进行类型化的,因而就有了 XML 数据修改语句中的命名空间声明。

UPDATE XmlCatalog
SET    Document.modify ('
    default namespace = "http://myBooks"
    replace value of (/bookstore/book[@ISBN=
"1-8610-0311-0"]/price)[1] with 49.99')

类型检查和静态错误

XQuery 引入了类型检查。编译阶段检查 XQuery 表达式和数据修改语句的静态类型正确性,并且将 XML 架构用于类型推理(在类型化 XML 的情况下)。假如表达式在运行时由于类型安全冲突而失败,则会产生静态类型错误。静态错误的例子有:将一个字符串与一个整数相加、在操作需要单值的地方接收一个值序列,以及查询不存在的节点来查找类型化数据。对于因类型不匹配而导致的静态错误,显式转换到正确的类型是一种变通方法。XQuery 运行时错误被转换成空序列。

假如编译器不能确定在运行时是否保证有 singleton 元素,则需要 singleton 元素的定位步骤、函数参数和运算符(例如,eq)就会返回错误。这种问题常常因非类型化的数据而产生。例如,查找属性需要 singleton 父元素;顺序选择单个的父节点就足够了。

例:value() 方法中的类型检查

下面的查询是在非类型化的 XML 列中进行的,它需要 //author/last-name 中的顺序规范,因为 value() 方法需要 singleton 节点作为第一个参数。假如没有,则编译器就不能确定在运行时是否只出现一个<last-name> 节点。

SELECT xCol.value('(//author/last-name)[1]', 'nvarchar(50)') LastName
FROM   docs

通过求 node()-value() 组合的值来提取属性值可以不需要顺序规范,如下一个示例所示。

例:已知的 singleton 元素

如下所示的 nodes() 方法为每个<book> 元素生成单独的行。对<book> 节点求值的 value() 方法提取 @genre 的值,@genre 作为一个属性,是 singleton 元素。

SELECT nref.value('@genre', 'varchar(max)') LastName
FROM   docs CROSS APPLY xCol.nodes('//book') AS R(nref)

跨域查询

假如数据驻留在关系数据类型的列和 XML 数据类型的列的组合中,就可能需要编写查询来组合关系数据处理和 XML 数据处理。通过使用带有 TYPE 指令的 FOR XML,可以将关系列和 XML 列中的数据转换成 XML 数据类型的实例,并使用 XQuery 对其进行查询。相反地,可以从 XML 值生成行集,并且使用 T-SQL 来对其进行查询,如下面的“从 XML 数据生成行集”一节所示。

编写跨域查询的一个更加方便和有效的方法是,使用 SQL 变量的值,或者使用 XQuery 或 XML 数据修改表达式中的列:

使用 sql:variable(),可以在 XQuery 或 XML DML 表达式中应用 SQL 变量的值。

通过 sql:column(),可以在 XQuery 或 XML DML 上下文中使用来自关系列的值。

这种方法允许应用程序参数化查询,如下面的示例所示。Sql:column() 的用法与前者类似,并且还带来其他的好处。正如基于成本的查询优化器所确定的,可以使用列的索引来提高效率。此外,也可以使用计算列。

XML 和用户定义的类型不允许与 sql:variable()sql:column() 一起使用。

例:使用 sql:variable() 的跨域查询

在这种查询中,<book> 元素的 ISBN 是使用 SQL 变量 @isbn 来传入的。代替使用常量,sql:variable() 提供 ISBN 的值,并且该查询可以用于搜索任何 ISBN,而不只是 ISBN 为 0-7356-1588-2 的 元素。

DECLARE @isbn varchar(20)
SET     @isbn = '0-7356-1588-2'
SELECT  xCol
FROM    docs
WHERE   xCol.exist ('/book[@ISBN = sql:variable("@isbn")]') = 1

从 XML 数据生成行集

在自定义属性管理和数据交换场景中,应用程序通常将 XML 数据的某些部分映射到行集。例如,为了将输入参数表传送到存储过程或函数,应用程序需要将数据转换成 XML,并且将其作为 XML 数据类型的参数传入。在存储过程或函数中,行集是从 XML 参数生成的。

SQL Server 2000 为此提供了 OpenXml()。它简化了从 XML 实例生成行集的过程,方法是指定行集的关系架构以及将 XML 实例内的值映射到行集中的列的方式。

另外,还可以使用 nodes() 方法来生成 XML 实例中的节点上下文,并且在 value()query()exist()nodes() 方法中使用该节点上下文来生成所期望的行集。nodes() 方法接受 XQuery 表达式,在 XML 列的每个 XML 实例中对其进行求值,并且有效地使用 XML 索引。下一个示例演示如何将 nodes() 方法用于行集生成。

例:从 XML 实例提取属性

假定要提取名字不是“David”的作者的名和姓,将其作为由两列(FirstName 和 LastName)组成的一个行集。通过使用 nodes()value() 方法,您可以达到此目的,如下所示:

SELECT nref.value('first-name[1]', 'nvarchar(50)') FirstName,
       nref.value('last-name[1]', 'nvarchar(50)') LastName
FROM   docs CROSS APPLY xCol.nodes('//author') AS R(nref)
WHERE  nref.exist('.[first-name != "David"]') = 1

在这个示例中,nodes('//author') 产生一个指向每个 XML 实例的 元素的引用的行集。作者的名和姓是通过对与这些引用有关的 value() 方法求值来获得的。要获得好的性能,需要建立 XML 列的索引,这是下一部分的主题。

建立 XML 数据的索引

XML 数据是以内部二进制形式存储的,存储容量可以达到 2 GB。每个查询在运行时一次或多次地解析表中每一行的 XML BLOB。这会使查询处理的速度变得很慢。假如在工作负荷中常常需要进行查询,则建立 XML 列的索引是有好处的,不过,这样做必须考虑在修改数据的过程中维护 XML 索引的成本。

XML 索引是通过一个新的 DDL 语句在类型化和非类型化的 XML 列中创建的。这为该列中的所有 XML 实例创建了一个 B+ 树。XML 列中的第一个索引是“主 XML 索引”。通过这个索引,可以在 XML 列中支持三种类型的次 XML 索引来加速普通类的查询,如下一节所述。

主 XML 索引

在基表(即定义 XML 列的表)的主键中,主 XML 索引需要聚集索引。它在 XML 节点的信息集项的一个子集中创建一个 B+ 树。B+ 树的列表示标记,例如元素和属性名称、节点值和节点类型。其他的列捕获 XML 数据中的文档顺序和结构,以及从 XML 实例的根节点到每个节点的路径,从而有效地对路径表达式进行求值。基表的主键在主 XML 索引中复制,以使索引行与基表行相关。

XML 架构中给定的标记和类型名称被映射为整数值,而映射值存储在 B+ 树中以优化存储。索引中的路径列按照相反的顺序(即从节点到 XML 实例的根)存储映射值的串联。当路径后缀已知时(在一个路径表达式中,如 //author/last-name),相反的表示使得可以匹配路径值。

假如对基表进行分区,则需要以相同的方式对主 XML 索引进行分区,也就是使用相同的分区函数和分区架构。

全部的 XML 实例都是从 XML 列检索的(SELECT * FROM docs 或 SELECT xCol FROM docs)。需要 XML 数据类型方法的查询使用主 XML 索引,从索引本身返回标量值或 XML 子树。

例:创建主 XML 索引

下面的语句在表 docs 的 XML 列 xCol 中创建一个名为 idx_xCol 的 XML 索引。

CREATE PRIMARY XML INDEX idx_xCol on docs (xCol)

次 XML 索引

一旦主 XML 索引创建完毕,就可以创建次 XML 索引来加速工作负荷中不同类的查询。三种类型的次 XML 索引 — PATH、PROPERTY 和 VALUE — 分别对基于路径的查询、自定义属性管理方案和基于值的查询有利。

PATH 索引在主 XML 索引的列 (path, value) 中构建 B+ 树。该路径的值是通过计算路径表达式和节点的值得出的,假如提供了一个路径值,则也可以使用所提供的值。在已知 PATH 索引开始字段的情况下,查找 PATH 索引可以加速路径表达式的求值。最常见的情况是在 SELECT 语句的 WHERE 子句中对 XML 列使用 exist() 方法。

PROPERTY 索引在主 XML 索引的列 (PK, path, value) 中创建 B+ 树,其中 PK 是基表的主键。此索引对 XML 实例中的属性值查找有利。

最后,VALUE 索引在主 XML 索引的列 (value, path) 中创建一个 B+ 树。此索引对节点的值已知但是其路径没有准确地在查询中指定的查询有利。这通常出现在祖先或自身 (descendant-or-self) 轴查询中,例如,//author[last-name="Howard"],其中, 元素可以出现在层次的任何一层。它还可以出现在“wildcard”查询中,例如 /book [@* = "novel"],其中,查询查找具有“novel”属性值的 元素。此外,VALUE 索引还可用于对类型化的 XML 进行基于值的范围扫描。

可以容纳多达 128 层的 XML 层次;在插入和修改的过程中,假如 XML 实例包含更长的路径,则会被拒绝。

类似地,可以建立一个节点值的前 128 个字节的索引;系统中可以容纳更长的值,但是不会建立索引。

例:基于路径的查找

假定下面的查询在工作负荷中是常见的:

SELECT xCol
FROM   docs
WHERE  xCol.exist ('/book[@genre = "novel"]') = 1

路径表达式 /book/@genre 和值“novel”对应于 PATH 索引的键字段。因此,类型 PATH 的次 XML 索引有助于此工作负荷:

CREATE XML INDEX idx_xCol_Path on docs (xCol)
   USING XML INDEX idx_xCol FOR PATH

例:获取对象的属性

考虑下面的查询,它从表 T 的每一行检索书的“genre”、“title”和 ISBN 属性:

SELECT xCol.value ('(/book/@genre)[1]', 'varchar(50)'),
    xCol.value ('(/book/title)[1]', 'varchar(50)'),
    xCol.value ('(/book/@ISBN)[1]', 'varchar(50)')
FROM    docs

属性索引可用于这种情况,该索引创建如下:

CREATE XML INDEX idx_xCol_Property on docs (xCol)
   USING XML INDEX idx_xCol FOR PROPERTY

例:基于值的查询

在下面的查询中,祖先或自己轴 (//) 指定了部分路径,这样,基于 ISBN 值的查询就可以从 VALUE 索引的使用中受益:

SELECT xCol
FROM    docs
WHERE    xCol.exist ('//book[@ISBN = "1-8610-0157-6"]') = 1

VALUE 索引创建如下:

CREATE XML INDEX idx_xCol_Value on docs (xCol)
   USING XML INDEX idx_xCol FOR VALUE

内容索引

可以在 XML 列中创建全文本索引;这会建立 XML 值内容的索引而忽略 XML 标记。属性值不是全文本索引的(因为它们被认为是标记的一部分),而元素标记被用作标记边界。可以在 XML 列中创建 XML 和全文本索引,并且组合使用全文本搜索和 XML 索引。使用全文本索引作为第一个筛选器来缩小选择范围,接着再应用 XQuery 进一步筛选。

使用 CONTAINS() 和 XQuery contains() 的全文本搜索有不同的语义。后者是子字符串匹配,而前者是使用词根检索的标记匹配。

例:在 XML 列中创建全文本索引

在 XML 列中创建全文本索引的步骤与在其他 SQL 类型列中创建全文本索引的步骤是非常相似的。需要在基表中有一个唯一的键列。DDL 语句如下,其中,PK__docs__7F60ED59 是该表的单列主键索引:

CREATE FULLTEXT CATALOG ft AS DEFAULT
CREATE FULLTEXT INDEX ON dbo.docs (xCol) KEY INDEX PK__docs__7F60ED59

例:组合全文搜索和 XML 查询

下面的查询检查 XML 值是否在书的标题中包含单词“Secure”:

SELECT * 
FROM   docs 
WHERE  CONTAINS(xCol,'Secure') 
AND    xCol.exist('/book/title/text()[contains(.,"Secure")]') =1

CONTAINS() 方法使用全文本索引来产生一个 XML 值的子集,它包含文档中所有的单词“Secure”。exist() 子句确保单词“Secure”出现在书的标题中。

使用 XML 索引执行查询

XML 索引可以加速查询的执行。查询总是针对 XML 列中的主 XML 索引进行编译(假如存在的话)。为整个查询(包括关系部分和 XML 部分)制订一个查询计划,该计划可以通过数据库引擎基于成本的优化器进行优化。根据查询优化器的成本估算来选择次 XML 索引。

XML 索引的目录视图

目录视图用于提供关于 XML 索引的元数据信息。目录视图 sys.indexes 包含带有索引“type”3 的 XML 索引项。“name”列包含 XML 索引的名称。

XML 索引也记录在目录视图 sys.xml_indexes 中,它包含所有的 sys.indexes 列以及一些对 XML 索引有意义的特别的列。列“secondary_type”中的值 NULL 表示主 XML 索引,值“P”、“R”和“V”分别代表 PATH、PROPERTY 和 VALUE 次 XML 索引。

可以从表值函数 sys.fn_indexinfo() 中找到 XML 索引的空间使用。它提供的信息包括所占用的磁盘页数、按字节计算的平均行大小、记录数,以及其他包括 XML 索引在内的所有索引类型的信息。这些信息可用于每个数据库分区;XML 索引使用与基表相同的分区架构和分区函数。

例:XML 索引的空间使用

SELECT sum(Pages)
FROM    sys.fn_indexinfo ('docs', 'idx_xCol_Path' , DEFAULT, 'DETAILED')

这产生在表 T 的所有分区中 XML 索引 idx_xCol_Path 所占用的磁盘页数。假如没有 sum() 函数,则结果将会返回每个分区的磁盘页使用。

XML 架构处理

在系统中,XML 架构是可选的。如前所述,没有绑定到 XML 架构的 XML 数据类型被认为是非类型化的;XML 节点值将作为 Unicode 字符串存储,而 XML 实例需要进行格式良好性检查。可以建立非类型化 XML 列的索引。

要类型化 XML,可以将 XML 数据类型与已注册到 XML 架构集合的 XML 架构相关联。新的 DDL 语句允许创建一个或多个 XML 架构可以注册到的 XML 架构集合。绑定到 XML 架构集合的 XML 列、参数或变量是根据该集合中的所有 XML 架构进行类型化的。在 XML 架构集合中,类型系统标识每个使用其目标命名空间的 XML 架构。

XML 实例中的每个顶层 XML 元素都必须指定它遵循的可能为空的目标命名空间。在插入和修改数据时,将根据每个顶层元素的目标命名空间对其进行有效性检查。二进制 XML 表示根据相关的 XML 架构信息对类型化值进行编码,并且是完整描述的,因此,与非类型化的 XML 相比,它的再解析效率更高一些。在 XML 索引中,还将对值进行适当的类型化(假如 /book/price 是在像 xs:decimal 这样的命名空间中定义的,则会存储为小数)。

在查询编译的过程中,使用 XML 架构进行类型检查,并且会因类型不匹配而引起静态错误。查询编译器还将 XML 架构用于查询优化。

数据库引擎的元数据子系统包含 XML 类型信息,例如 XML 架构集合和它们所包含的 XML 架构,以及基本 XSD 和关系类型系统之间的映射。几乎所有的 W3C XML 架构 1.0 规范(请参阅 http://www.w3.org/TR/2001/REC-xmlschema-1-20010502/ 和 http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/)都支持。XML 架构文档中的注释和注解都没有保留,并且不支持 key/keyref。

XML 架构集合

XML 架构集合是一个元数据实体(通过关系架构定义作用域),并且包含一个或多个相关(例如,使用<xs:import>) )或不相关的 XML 架构。XML 架构集合中单独的 XML 架构是使用其目标命名空间进行标识的。XML 架构集合是安全的实体,大致与表类似。

XML 架构集合是使用 CREATE XML SCHEMA COLLECTION 语义创建的,提供一个或多个 XML 架构。然后可以使用 XML 架构集合来类型化 XML 列。这种设计产生了灵活的数据模型,其中,按照不同的 XML 架构类型化的 XML 可以存储在相同的列中。当 XML 架构的数量很大时,这尤其方便。此外,这种设计还支持对 XML 架构进行一定程度的扩展。

另外,类型化的 XML 列中的选项 DOCUMENT 或 CONTENT 分别指定了 XML 树或片段是否可以存储在 XML 列中。默认为 CONTENT。对于 DOCUMENT,每个 XML 实例都必须指定其顶层元素的目标命名空间,该 XML 实例就是按照这个命名空间来进行有效性检查和类型化的。而对于 CONTENT,每个顶层元素都可以指定 XML 架构集合中的任何一个目标命名空间。该 XML 实例按照出现在实例中的所有目标命名空间进行有效性检查和类型化。

例:创建 XML 架构集合

假定要使用带有目标命名空间 http://myBooks 的 XML 架构来类型化 XML 实例。可以创建一个 XML 架构集合 myCollection 并将该 XML 架构作为 myCollection 的内容,如下所示:

CREATE XML SCHEMA COLLECTION myCollection AS 
'<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
  xmlns="http://myBooks" 
   elementFormDefault="qualified"
  targetNamespace="http://myBooks">
  <xsd:element name="bookstore" type="bookstoreType" />
  <xsd:complexType name="bookstoreType">
   <xsd:sequence maxOccurs="unbounded">
     <xsd:element name="book" type="bookType" />
   </xsd:sequence>
  </xsd:complexType>
  <xsd:complexType name="bookType">
   <xsd:sequence>
     <xsd:element name="title" type="xsd:string" />
     <xsd:element name="author" type="authorName" />
     <xsd:element name="price" type="xsd:decimal" />
   </xsd:sequence>
   <xsd:attribute name="genre" type="xsd:string" />
   <xsd:attribute name="publicationdate" type="xsd:string" />
   <xsd:attribute name="ISBN" type="xsd:string" />
  </xsd:complexType>
  <xsd:complexType name="authorName">
   <xsd:sequence>
     <xsd:element name="first-name" type="xsd:string" />
     <xsd:element name="last-name" type="xsd:string" />
   </xsd:sequence>
  </xsd:complexType>
</xsd:schema>'

同时也为该 XML 架构注册到的 myCollection 创建了一个新的元数据实体。

修改 XML 架构集合

ALTER XML SCHEMA COLLECTION 语句支持用新的顶层架构组件来扩展 XML 架构,并且还支持将新的 XML 架构注册到 XML 架构集合。下面的示例对此进行了演示。

例:改变 XML 架构集合

下面的语句显示了如何将具有目标命名空间 http://myDVD 的新 XML 架构添加到 XML 架构集合 myCollection 中:

ALTER XML SCHEMA COLLECTION myCollection ADD
'<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
  xmlns="http://myDVD" 
   elementFormDefault="qualified"
  targetNamespace="http://myDVD">
  <xsd:element name="dvdstore" type="dvdstoreType" />
  <xsd:complexType name="dvdstoreType">
   <xsd:sequence maxOccurs="unbounded">
     <xsd:element name="dvd" type="dvdType" />
   </xsd:sequence>
  </xsd:complexType>
  <xsd:complexType name="dvdType">
   <xsd:sequence>
     <xsd:element name="title" type="xsd:string" />
     <xsd:element name="price" type="xsd:decimal" />
   </xsd:sequence>
   <xsd:attribute name="genre" type="xsd:string" />
   <xsd:attribute name="releasedate" type="xsd:string" />
  </xsd:complexType>
</xsd:schema>'

XML 架构集合的目录视图

XML 架构集合的 SQL 目录视图允许用户重新构造单独的 XML 架构命名空间的内容。XML 架构集合在目录视图 sys.xml_schema_collections 中枚举。XML 架构集合“sys”由系统定义,并且包含一些预定义的命名空间,这些命名空间可用于所有用户定义的 XML 架构集合,而不必显式地加载它们。该列表包含用于 xml、xs、xsi、fn 和 xdt 的命名空间。

值得一提的其他两个目录视图是:sys.xml_schema_namespaces,它枚举每个 XML 架构集合中的所有命名空间;sys.xml_components,它枚举每个 XML 架构中的所有 XML 架构组件。

内置函数 XML_SCHEMA_NAMESPACE() 接受关系架构和 XML 架构集合的名称,以及 XML 架构的目标命名空间(可选)。它返回包含该 XML 架构的 XML 数据类型实例。假如目标命名空间参数被省去,那么该内置函数返回的 XML 实例就会包含 XML 架构集合中除预定义 XML 架构以外的所有 XML 架构。

例:枚举 XML 架构集合中的 XML 命名空间

将下列查询用于 XML 架构集合“myCollection”:

SELECT XSN.name
FROM    sys.xml_schema_collections XSC 
 JOIN sys.xml_schema_namespaces XSN ON 
(XSC.xml_collection_id = XSN.xml_collection_id)
WHERE    XSC.name = 'myCollection'   

例:从 XML 架构集合输出指定的 XML 架构

下面的语句从(关系)架构 dbo 中的 XML 架构集合“myCollection”输出带有目标命名空间 http://myBooks 的 XML 架构。

SELECT XML_SCHEMA_NAMESPACE (N'dbo', N'myCollection', 
N'http://myBooks')

对 XML 架构集合的访问控制

通过使用 SQL Server 2005 的安全模型,可以像任何 SQL 对象一样保护 XML 架构集合。您可以授予用户在数据库中创建 XML 架构集合的特权。每个 XML 架构集合都支持 ALTER、CONTROL、TAKE OWNERSHIP、REFERENCES、EXECUTE 和 VIEW DEFINITION 等权限。

执行 ALTER XML SCHEMA COLLECTION 语句需要 ALTER 权限。

假如要通过执行 ALTER AUTHORIZATION 语句将 XML 架构集合的所有权从一个用户转移到另一个用户,则需要 TAKE OWNERSHIP 权限。

REFERENCES 权限授权用户在任何需要架构绑定的地方使用 XML 架构,例如,类型化或约束 XML 列和参数。

要检查用户针对 XML 架构集合插入或更新的值的有效性,则需要 EXECUTE 权限。使用 XML 数据类型从类型化的 XML 列、变量和参数查询值也需要此权限。

VIEW DEFINITION 权限允许用户访问目录视图中与 XML 架构集合(所有的 XML 架构都包含其中)对应的行,所有 XML 架构组件都包含在这些 XML 架构中。

CONTROL 权限授予用户执行 XML 架构集合中任何操作的权限,包括使用 DROP XML SCHEMA COLLECTION 语句撤消 XML 架构集合。这意味着具有 XML 架构集合的其他权限。

除了表或 XML 列中的其他权限以外,还需要 XML 架构集合的权限。用户需要权限来创建表,并且还需要 XML 架构集合 C 中的 REFERENCES 权限来创建包含 XML 列 X(按照 C 类型化)的表 T。具有将数据插入列 X 的权限的用户可以这样做,前提是用户具有 XML 架构集合 C 中的 EXECUTE 权限。类似地,要使用 XML 数据类型方法来查询列 X 中的数据,用户需要列 X 中的 SELECT 权限和列 C 中的 EXECUTE 权限。然而,对于从列 X 检索整个 XML 值,列 X 中的 SELECT 权限就足够了,例如 SELECT X FROM T 或 SELECT * FROM T。

可以从用户撤回权限,还可以通过 SQL Server 2005 的安全模型拒绝用户的权限,正如允许其拥有权限一样。

目录视图的可视化

XML 架构集合中具有 ALTER、TAKE OWNERSHIP、REFERENCES、VIEW DEFINITION 或 CONTROL 权限的用户可以在目录视图中访问 XML 架构集合的行及其所包含的 XML 架构和它们的 XML 架构组件。该主体还可以通过使用内置函数 XML_SCHEMA_NAMESPACE() 来访问 XML 模式集合的内容。

假如用户的 VIEW DEFINITION 权限被拒绝,则该用户就不能在目录视图中或通过使用 XML_SCHEMA_NAMESPACE() 访问 XML 架构集合。

对 FOR XML 的增强

可以将 TYPE 指令生成的 XML 数据类型实例分配给 XML 列、变量或参数,或者使用 XML 数据类型方法对其进行查询。这使得可以嵌套 SELECT ...FOR XML TYPE 语句。

PATH 模式允许用户指定需要出现列值的 XML 树中的路径,并且与上述嵌套一起使用时更易于编写(与 FOR XML EXPLICIT 相比),不过,它也不可用于深层次。

与 ELEMENTS 一起使用的指令 XSINIL 可以将 NULL 映射为具有属性 xsi:nil="true" 的元素。新的 ROOT 指令允许在所有的 FOR XML 模式下指定根节点。新的 XMLSCHEMA 指令生成 XSD 内联架构。

要获得更多关于改进和示例的信息,请参阅由 Michael Rys 撰写的“What's New in FOR XML in Microsoft SQL Server”(MSDN 于 2004 年 6 月发布)。

性能指导原则

XML 数据模型比关系数据模型更加丰富,因而也更加复杂。XML 数据模型不仅允许对复杂的数据进行建模,而且还必须保留层次关系和数据中的文档顺序。文档顺序是通过基于 XML 节点标识符的排序维护的;这同时维护了层次关系。所有这些都对更复杂的查询计划产生了影响。

为了获得更好的性能,应该将结构化数据存储在表的关系列中。假如数据是半结构化或非结构化的,则需要选择使用 XML 数据模型来进行建模,并且加入 XML 标记,不过,别指望会有什么好的性能。XML 架构有助于查询优化。

SQL Server CLR 中的 XML 支持

通过使用 SQL Server CLR 支持,可以在托管代码中编写服务器端逻辑来执行业务规则。可以采用多种方法来将这些业务逻辑添加到 XML 数据中:

可以在 XML 值传入的托管代码中编写 SQLCLR 函数,并且使用 System.Xml 命名空间提供的 XML 处理功能。这样的一个例子是将 XSL 转换应用于 XML 数据,如下所述。另外,还可以将 XML 反序列化成一个或多个托管类,并且使用托管代码对它们进行操行。

可以编写 T-SQL 存储过程和函数来调用 XML 列中的处理,以满足您的业务需要。

例:应用 XSL 转换

考虑接受 XML 数据类型实例的 CLR 函数 TransformXml() 和存储在文件中的 XSL 转换,将该转换应用到 XML 数据,并返回已转换的 XML 结果。用 C# 编写的主干函数如下:

public static SqlXml TransformXml (SqlXml XmlData, string xslPath) {
   // Load XSL transformation
   XslTransform xform = new XslTransform();
   XPathDocument xslDoc = new XPathDocument (xslPath);
   xform.Load (xslDoc.CreateNavigator(),null);

   // Load XML data 
   XPathDocument xDoc = new XPathDocument (XmlData.CreateReader());
   XPathNavigator nav = xDoc.CreateNavigator ();

   // Return the transformed value
   SqlXml retSqlXml = new SqlXml (xform.Transform(nav, null));
   return (retSqlXml);
} 

一旦注册了程序集并创建了用户定义的 T-SQL 函数 SqlXslTransform()(与 TransformXml() 相对应),就可以从 T-SQL 调用该函数,如下面的查询所示:

SELECT SqlXslTransform (xCol, 'C:\yukon\xsltransform.xsl')
FROM    T
WHERE  xCol.exist('/book/title/text()[contains(.,"custom")]') =1

查询结果包含已转换的 XML 的行集。

SQLCLR 开辟了一个全新的世界,可用于将 XML 数据分解成表或属性提升,并且使用 System.Xml 命名空间中的托管类来查询 XML 数据。更多的信息可以在SQL Server 2005 和 Visual Studio 2005 在线书籍中找到。

四、SQL Server 2005 中的客户端 XML 处理

XML 数据类型的客户端支持

.NET 框架 V2.0 中的 ADO.NET XML 支持

SqlDataReader.GetSqlXml() 方法的 System.Data.SqlTypes 命名空间中,XML 数据类型是作为类 SqlXml 公开的。可以使用 SqlXml.CreateReader() 函数从 SqlXml 对象获得 XmlReader。

类型化 XML 列的 XML 架构集合的名称由三部分组成,可以从 XML 列的元数据中获得(通过使用 SqlDataReader 对象中的 GetSchemaTable() 或 GetSqlMetaData (int)),其三个部分作为三个属性,分别表示数据库 (XmlSchemaCollectionDatabase)、关系架构 (XmlSchemaCollectionOwingSchema) 和 XML 架构集合 (XmlSchemaCollectionName) 的名称。

客户端可以使用新的架构行集 XMLSCHEMA 从服务器检索 XML 架构。XMLSCHEMA 行集包含 XML 架构集合、目标命名空间和 XML 架构内容本身这样三列。

下面的示例显示了用于对 XML 数据类型进行托管访问的主干代码。

例:对 XML 数据类型进行进程内访问

下面的 C# 代码演示了如何从进程内提供程序访问 XML 数据类型。用于 SQL 客户端访问的代码需要适当地改变进程内访问。

using System;
using System.Xml;
using System.Data;
using System.Data.SqlTypes;
using System.Data.SqlServer;

class xmldtADONETReadAccessInProc
{
   static void ReadXmlDataType () {
      // in-proc connection to server
      SqlConnection conn = SqlContext.GetConnection();

      // prepare query to select xml data
      SqlCommand cmd = conn.CreateCommand();
      cmd.CommandText = "SELECT xCol FROM docs";

      // execute query and retrieve incoming data
      SqlDataReader r = cmd.ExecuteReader();
      r.Read();

      // access XML data type field in rowset
      SqlXml xml = r.GetSqlXml(0);
      new XmlTextWriter(Console.Out).WriteNode( 
xml.CreateReader(), true);
   }
}

例:使用 SQL 客户端提供程序更新 XML 数据类型列

下面的代码所显示的 WriteXmlDaraType() 方法使用 SQL 客户端程序提供程序来替换 XML 列中的值。用于进程内提供程序的代码是相似的。

using System;
using System.Xml;
using System.Data;
using System.Data.SqlTypes;
using System.Data.SqlClient;

class xmldtADONETUpdateAccess
{
   static void WriteXmlDataType () {
      // connection to server
      SqlConnection conn = new SqlConnection("server=server1;" +
" database=XMLtest; Integrated Security=SSPI");
      conn.Open();

   // update XML column at the server
   SqlCommand cmd = conn.CreateCommand();
   cmd.CommandText = "UPDATE docs SET xCol=@x WHERE id=1";

// set value of XML parameter
   SqlParameter p = cmd.Parameters.Add("@x", SqlDbType.Xml);
p.Value = new SqlXml(new XmlTextReader("<hello/>", 
XmlNodeType.Document, null));

// execute update and close connection
   cmd.ExecuteNonQuery();
conn.Close();
}
}

SQL 本机客户端访问

在新的 SQL 本机访问 (SQLNCLI) 的 OLE DB 提供程序中,可以通过 ISequentialStream 来将 XML 数据类型列作为 Unicode 字符串(DBTYPE_XML、DBTYPE_BSTR、DBTYPE_WSTR 和 DBTYPE_VARIANT)或 Unicode 字符串流进行检索。默认为 DBTYPE_XML。

由三部分组成的 XML 架构集合名称是在由 IDBSchemaRowset::GetRowset() 返回的 COLUMNS 架构行集的三个新列中携带的:SS_XML_CATALOGNAME 给出目录的名称;SS_XML_SCHEMANAME 给出 XML 架构集合所驻留的关系架构的名称;SS_XML_SCHEMACOLLECTIONNAME 给出 XML 架构集合的名称。这些名称都属于 DBTYPE_WSTR 类型。对于非类型化的 XML 列,这些列具有 NULL 值。

可以对 PROCEDURE_PARAMETERS 架构行集和 IColumnRowset:GetColumnRowset() 进行类似的改变。

要检索 XML 架构集合的内容,客户端可以通过在对 XML_SCHEMA_NAMESPACE() 的调用中使用这些名称来单独访问服务器,并且返回 XML 架构作为 XML 数据类型。另外,带有新的 SS_XMLSCHEMA 架构行集的 IDBSchemaRowset 可以返回目录的名称、关系架构的名称、XML 架构集合的名称、目标命名空间和 XML 架构。

要使用 SQLNCLI 进行 ODBC 访问,需要将 XML 数据类型映射到名为 SQL_SS_XML 的 Unicode 可变长度字符数据。由三部分组成的 XML 架构集合的名称是通过用于 XML 列的 SqlColAttribute 作为 CharacterAttributePtr 出现的。这些字段标识符为 SQL_DESC_SS_XML_SCHEMA_CATALOG_NAME、SQL_DESC_SS_XML_SCHEMA_SCHEMA_NAME 和 SQL_DESC_SS_XML_SCHEMA_COLLECTION_NAME,分别用于数据库、关系架构和 XML 架构集合的名称。

用户必须安装 SQL Server 2005 版的数据库服务器或客户端工具才能获取 SQL 本机客户端。在撰写本文时,还不能使用 MDAC 2.8 ADO 对 XML 数据类型进行 OLE DB 访问。

SQLXML — XML 和关系架构之间的映射

可以使用 SQLXML 映射技术来创建关系数据的逻辑 XML 视图。XML 视图(也称为“映射”或“带注解的架构”)是通过将特殊的注解添加到特定的 XSD 架构中创建的。于是,其他的 SQLXML 技术就可以使用这种带注解的架构,将针对逻辑 XML 视图进行的查询和更新转换成针对关系表进行的查询和更新:

当 XML 视图与 XPath 查询组合在一起时,SQLXML 将生成 FOR XML 查询来查找请求数据,并且根据架构的指定对其进行构形。

SQLXML Updategrams 表示对 XML 实例进行的更改,当与带注解的架构组合在一起时,将使用开放式并发将这些更改重新保存到关系更改中,以确保更新正确的数据。

SQLXML Bulkload 使用 XML 视图将 XML 数据“切碎”并存放到关系表中。

有关这些主题的更多信息,请访问 SQLXML documentation。

创建关系表的 XML 视图

要创建数据库的 XML 视图,需要从用于 XML 数据的 XSD 架构开始。数据库表/视图中的行将映射为该架构中复杂类型的元素。数据库中的列值映射为属性或简单类型的元素。

默认情况下,假如没有给出显式注解,SQLXML 就会假定复杂类型的元素将映射到表,而简单类型的元素和属性将映射到列。只有当元素和属性的名称与数据库中表和列的名称完全相同时,这才有效。

假如元素/属性的名称与它映射到的表/视图或列的名称不同,则必须创建显式映射。下面的注解用于指定 XML 文档中的元素或属性间的映射和数据库中的表(视图)或列之间的映射:

sql:relation — 将 XML 元素映射到数据库表。

sql:field — 将元素或属性映射到数据库列。

通过映射关系创建 XML 视图中的层次

在数据库中,表可以通过外键关系来关联。在 XML 中,这些相同的关系是通过嵌套的元素层次来表示的。为了在映射中构造正确的嵌套,必须指定关联元素的方式。可以使用 sql:relationship 注解在映射架构元素中建立这些关系。在这种注解中,可以指定父表和子表,以及每个表中需要用于执行加入的列。然后,SQLXML 将利用这些信息为映射构造正确的嵌套层次。

使用溢出存储未使用的数据

当 XML 数据有正规的结构时映射是有效的。然而,在 XML 中,可能有一些数据是非结构化的,还可能有一些数据没有映射到特定的列。要存储这种数据并随后检索它,可以使用 sql:overflow 注解。sql:overflow 注解指定存储所有未使用数据的列以及查询时从何处检索数据。

通过溢出列,还可以扩展 XML,而不必将其添加到数据库。可以在任何时候将元素和属性添加到 XML 结构,而不必在数据库中添加列来存储它们。可以将它们简单地存储到溢出字段,并且在适当的时候对其进行检索。

更多信息

有关创建 XML 视图和映射示例的详细信息,请参阅 Creating XML Views by Using Annotated XSD Schemas。

使用 XPath 来查询 XML 视图

一旦创建好数据库的 XML 视图,就可以使用 XPath 查询语言来查询视图,就好像它是实际的 XML 文档一样。SQLXML 支持 XPath 1.0 查询语言中的一个子集。当针对映射进行 XPath 查询时,SQLXML 将它们组合在一起,并且创建发送到 SQL Server 的 FOR XML EXPLICIT 语句。然后检索正确的数据,并根据映射对其进行构形。

请参阅 SQLXML 文档以获得关于 XML 视图中支持的 XPath 子集的详细信息。

使用 Updategrams 通过 XML 视图进行更新

可以通过 XML 视图修改(插入、更新或删除)SQL Server 中的数据库,方法是针对数据库的 XML 视图使用 Updategram。

updategram 的结构

updategram 是一个 XML 文档,带有<sync>、<before> 和<after> 元素,这些元素构成了 updategram 的语法。每个<sync> 块包含一个或多个<before> 和<after> 块。<before> 标识记录实例的现有状态(也称为“前状态”)。<after> 标识要更改的数据的新状态。updategram 是删除、插入还是更新记录实例取决于<before> 和<after> 块的内容。

插入操作

当记录实例出现在<after> 块而没有出现在相应的<before> 块中时,updategram 指示插入操作。在这种情况下,updategram 将<after> 块中的记录插入数据库。

删除操作

当记录实例出现在<before> 块而没有相应的记录出现在<after> 块中时,updategram 指示删除操作。在这种情况下,updategram 从数据库中删除<before> 块中的记录。

假如在 updategram 中指定的元素或者匹配表中的多行或者不匹配表中的任何行,则 updategram 会返回错误,并取消整个<sync> 块。updategram 中的元素一次只能删除一条记录。

更新操作

当更新现有的数据时,必须指定<before> 和<after> 块。updategram 使用<before> 块中指定的元素来标识数据库中的现有记录。<after> 块中相应的元素指示在执行更新操作之后记录的外观应该怎样。

<before>块中的元素必须只与数据库中的一个表行相匹配。假如该元素匹配多个表行或不匹配任何表行,则 updategram 都会返回错误,并且取消整个<sync> 块。

更多信息

要获得更多关于通过 XML 视图创建和使用 updategram 修改数据的信息,请参阅 Using Updategrams to Modify Data。

通过 XML 视图大量加载 XML 数据

XML Bulk Load 是一个 COM 对象,它允许您将 XML 数据加载到 SQL Server 表中。虽然通过使用 INSERT 语句和 OPENXML 函数可以将 XML 数据插入 SQL Server 数据库中,但是当需要插入大量的 XML 数据时,这种大量加载实用程序提供了更高的性能。XML Bulk Load 解释映射架构并标识 XML 数据要插入的表。然后,它将 XML 数据“切碎”并存放到关系表中。

在进行大量加载处理时,由于源 XML 文档可能很大,所以并不将整个文档读入内存。相反,XML Bulk Load 将 XML 数据解释为流,并且边解释它边读取它。当该实用程序读取数据时,它标识数据库表,从 XML 数据源生成适当的记录,然后将记录发送到 SQL Server,以供插入。

要获得更多关于 Bulk Load 如何工作和使用的信息,请参阅 Performing Bulk Load of XML Data。

SQLXML 数据访问方法

由于有了 SQL Server 2000,所以又增加了两种访问 SQLXML 功能的新方法:

SQLXML 托管类

SQLXML Web 服务

另外,还增强了对 SQL Server 的 HTTP 访问,以提供对模板中的 Updategram 的支持。

SQLXML 托管类

SQLXML 托管类公开 Microsoft .NET 框架内的 SQLXML 3.0 的功能。通过 SQLXML 托管类,可以编写 C# 应用程序从 SQL Server 的实例访问 XML 数据,将数据带入 .NET 框架环境,处理数据,并将更新重新发送回 SQL Server 以应用这些更新。

要获得更多关于如何使用 SQLXML 托管类的细节,请参阅 SQLXML .NET Support。

SQLXML Web 服务

SQLXML 中的 Web 服务支持将 SQL Server 作为 Web 服务公开,从而为客户端提供 SQL Server 功能。可以将 SOAP HTTP 请求发送到正在运行 SQLXML 的服务器,以执行存储过程、用户定义的函数 (UDF) 和模板。

通过使用所包括的用于 SQL Server 实用程序的 IIS 虚拟目录管理,也可以设置 Web 服务。一旦 Web 服务虚拟目录设置完毕,就可以将存储程序和模板添加到站点。然后,客户端就可以通过 HTTP 之上的 SOAP 协议来访问这些服务。

SQL Server 2005 现在包括对服务器中的 Web 服务的本机支持。然而,只有 SQLXML 支持通过 Web 服务访问 XML 模板。另外,可以在不同于数据库服务器的服务器上设置 SQLXML,创建远离数据库的单独中间层盒子。

要获得更多关于 SQLXML Web 服务的细节,请访问 Web Services (SOAP) Support in SQLXML。

.NET 框架 2.0 中的 XQuery

为了在 SQL Server 2005 中补充引入 XQuery,在 Visual Studio 2005 Beta 的 .NET 框架 V2.0 版中,System.Xml.Query 命名空间内置了一个中间层 XQuery 处理器。在这一部分中,我们说明该处理器如何提供查询和聚合完全不同的数据源的能力,例如文件和关系数据存储 SQL Server 数据库。

简单的说,下面的代码示例显示了一个查询,用于选择书店中所有 genre 属性值为 autobiography 的书,并且将这些书的标题作为一个新的书店写出。执行查询的称为“books.xml”的本地文件被加载,而将结果写入另一个称为“output.xml”的本地文件。

using (XmlWriter writer = XmlWriter.Create("output.xml"))
{
XQueryCommand xq = new XQueryCommand();
   string query =
         "<bookstore>" +
         "{ for $s in /bookstore/book " +
         "where $s/@genre='autobiography' " +
         "return $s/title }" +
         "</bookstore>";
   xq.Compile(query);
   xq.Execute("books.xml", new XmlUrlResolver(), writer);
}

查询表达式如下:

<bookstore>
{for $s in /bookstore/book
where $s/@genre='autobiography'
return $s/title}
</bookstore>

XQueryCommand 类是 XQuery 处理器。首先由 Compile 方法编译查询表达式,该方法对值进行类型检查,并且生成一个可执行文件。Execute 方法执行接受 books.xml 文档作为输入的查询。

在 SQL Server(具有将 T-SQL 语句内嵌到 XQuery 表达式的功能)中,XQueryCommand 类还能够对关系表中的数据进行查询。事实上,这使得能够将两个不同的查询语言之间的集成运用于不同的域,并且还补充了反向 SQL Server 2005,其中,T-SQL 语句内嵌到 XQuery 语句。下面的代码示例显示了一个查询,用于从样本 Northwind 数据库中选择所有的顾客。

declare namespace sql='http://schemas.microsoft.com/framework/2003/xml/sql';
<Customers>{
$for $customer in sql:query("select * from customers", "conn")/*
return customer
}</Customers>

sql:query 方法使您能够对连接进行 T-SQL 查询,其结果以与 XQuery doc() 函数完全相同的风格作为 XML 文档返回。假如结果是由关系类型组成的,则在客户端作为有效流转换成 XML。假如 SQL 查询的结果已经是 XML,例如 FOR XML 查询或 XML 数据类型的查询,则使用该查询的结果。假如没有返回结果,像在大多数 T-SQL DML 语句的情况下,这解释为空文档,它是有效的。来自此 XQuery 的输出如下所示,它列出了用 Customers 元素包装的顾客表中所有的顾客。在结果中自动为表中的每一行创建行元素。

<Customers>
  <row>
    <CustomerID>ALFKI</CustomerID>
    <CompanyName>Alfreds Futterkiste</CompanyName>
    <ContactName>Maria Anders</ContactName>
    <ContactTitle>Sales Representative</ContactTitle>
    <Address>Obere Str. 57</Address>
    <City>Berlin</City>
    <PostalCode>12209</PostalCode>
    <Country>Germany</Country>
    <Phone>030-0074321</Phone>
    <Fax>030-0076545</Fax>
  </row>
  <row>
    <CustomerID>ANATR</CustomerID>
    <CompanyName>Ana Trujillo Emparedados y helados</CompanyName>
    <ContactName>Ana Trujillo</ContactName>
    <ContactTitle>Owner</ContactTitle>
    <Address>Avda. de la Constitucion 2222</Address
    <City>Mexico D.F.</City
    <PostalCode>05021</PostalCode>
    <Country>Mexico</Country>
    <Phone>(5) 555-4729</Phone>
    <Fax>(5) 555-3745</Fax>
  </row>
...
</Customers>

为了通过 SQL Server 执行此查询,需要使用下面的代码。

static String ConnectionString = "database=Northwind;Data Source=localhost;Integrated Security=SSPI";

using (SqlConnection connection = new SqlConnection(ConnectionString))
{
       XmlDataSourceResolver resolver = new XmlDataSourceResolver();
   resolver.Add("conn", connection);
   XQueryCommand xq = new XQueryCommand();
   
StreamReader reader = new StreamReader("query.xml");
   xq.Compile(reader);
   
XmlWriterSettings settings = new XmlWriterSettings();
   settings.Indent = true;
   settings.ConformanceLevel = ConformanceLevel.Auto;
   using (XmlWriter writer = XmlWriter.Create("output.xml", settings))
   {
      try
      {
         connection.Open();
         xq.Execute(resolver, writer);
      }
      catch (Exception e)
      {
         Console.Out.WriteLine(e.Message);
         Console.Out.WriteLine(e.InnerException.Message);
      }
   }
}

这里,XmlDataSourceResolver 类提供使数据库连接与 sql:query 调用内可以使用的名称相关联的角色。首先从名为 query.xml 的本地文件中读取该查询,再进行编译,然后创建 XmlWriter 来写入结果。需要注意 XmlWriter 中使用的 ConformanceLevel.Auto 设置,它自动检测创建片段还是创建文档,这是必要的,因为可以借此构造结果中的元素,XQuery 能够生成这两者。在执行查询之前,向数据库打开连接,然后执行查询,并将结果写入文件。

sql:query 命令极其灵活而强大,它能够将有名称的参数提供给服务器上的 T-SQL 查询或调用的存储过程。sql:query 中命名的参数的作用完全等同于 System.Data.SqlClient 类。参数是在 T-SQL 查询中提供的,由后面紧跟名称的“@”表示。然后提供该名称和实际值之间的绑定作为查询的参数。对于每个命名的值,可以将相应的值(由序列中的位置决定)用于绑定。必须有与语言绑定数量相同的参数名称。下面的示例最好地对此进行了演示。

declare namespace sql='http://schemas.microsoft.com/framework/2003/xml/sql';
let $country := "USA"
<Customers>{
for $customer in sql:query("select * from customers where Country = @country",
"conn", {"@country"}, {$country})
return $customer/PostalCode
}</Customers>

这里,将参数 country 提供给 T-SQL 查询,从而将 $country XQuery 变量绑定到 @country 参数。结果是所有美国顾客的邮政编码列表。

sql:query 函数还可用于调用 T-SQL 存储过程(假如需要的话,就以上述方式传送参数)。下面的示例显示了如何调用 Northwind 数据库中的“Ten Most Expensive Products”存储过程,它没有输入参数。

declare namespace sql ='http://schemas.microsoft.com/framework/2003/xml/sql';
for $prod in sql:query('Exec "Ten Most Expensive Products"', "conn")
return $prod
小节

本文描述了 SQL Server 2005 中 XML 的补充技术。服务器端功能包括 XML 存储、索引和查询处理的本机实现。现有的功能(例如 FOR XML 和 OpenXML)已经得到增强。客户端支持包括增强 ADO.NET 来支持 XML 数据类型,以及在 System.Xml 中支持 XQuery 查询不同的 XML 源。另外,SQLXML 映射技术的 Web 版增强现在已经合并到 SQL Server 2005 中。

客户端和服务器端支持可用于不同的情况。XML 数据类型提供了一种简单的机制来存储 XML 数据,方法是将 XML 数据插入非类型化的 XML 列。使用 XML 架构定义类型化的 XML 有助于数据库引擎优化存储和查询处理,另外,还可以提供数据验证。

XML 数据类型保留了文档顺序,它对于像文档管理这样的应用程序非常有用。它还可以处理递归的 XML 架构。对于带有已知架构的结构化数据,关系数据模型仍然是最佳的选择。[n]varchar(max) 正好适合于查询和更新不重要的情况。

只要是在服务器上对存储在表中的关系数据使用以 XML 为中心的编程模型,就可以使用 SQLXML 映射技术。映射基于将 XML 架构定义为 XML 视图。映射可用于将 XML 数据大量加载到表以及使用 XPath 1.0 来查询表。SQLXML 映射技术不保留文档顺序,因此可用于 XML 数据处理而不是 XML 文档处理。

在即将发布的 .NET 框架 V2.0 Beta 1 版中,System.Xml 内的核心 XML 类支持读、写、操作和转换 XML。通过改进性能、可用性、类型化和查询,V2.0 版对 XML 的支持在创新性、标准支持和易用性方面继续保持行业领先。这包括用于操作 SQL Server 外的 XML 源的 XQuery 处理器在内。

服务器端技术和客户端技术互为补充。映射技术可以利用服务器端功能来增加其功能,例如维护文档顺序和递归架构,以及找到更多的应用领域。另一方面,CLR 将可扩展性和现有 XML 工具(如 XSLT 转换)的价值带入了 XML 数据类型。服务器和客户端中的 XQuery 实现彼此之间以及与 2003 年 11 月发布的 XQuery 规范草稿之间都是一致的。XML 架构实现在服务器端实现和中间层实现之间也是一致的。

Shankar Pal SQL Server 引擎的程序经理,主要研究服务器端 XML 技术。

Mark Fussell Microsoft WebData 组的首席程序经理,该小组负责开发 Microsof 的数据访问技术,其中包括 .NET 框架的 System.Xml System.Data 命名空间中的组件、Microsoft XML 核心服务 (MSXML) 以及 Microsoft 数据访问组件 (MDAC)。他的网络日记存放在http://weblogs.asp.net/mfussell

Irwin Dolobowsky 是 Webdata 组的程序经理。