本文探讨 SQL Server 2005 中内置的 XML 支持。描述了这种支持如何与 .NET 框架 V2.0 和本机代码(例如 OLEDB 和 SQLXML)均支持的客户端编程相集成。
一、简介
可扩展标记语言 (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 支持,以及随后发布的 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 数据。在 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 的索引。
非类型化的 XMLSQL 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: empxmlfile.xml 的内容作为 BLOB。在表 docs 中新插入一行,值 10 作为主键,而 BLOB 作为 XML 列 xCol。格式良好性检查出现在文件内容分配到 XML 列时。
INSERT INTO docs
SELECT 10, xCol
FROM (SELECT * FROM OPENROWSET
(BULK 'C: empxmlfile.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 子句中的条件,它就会被忽略,执行转到下一行。