Microsoft SQL Server 2005 的 XML 最佳实施策略
作者: Shankar Pal、Vishesh Parikh、Vasili Zolotov、Leo Giakoumakis、Michael Rys
Microsoft Corporation
适用于:
Microsoft SQL Server
摘要:了解 SQL Server 2005 中的 XML 数据建模和使用准则,并观察一些说明性的示例。
本页内容
简介
数据建模
使用 XML 数据类型进行数据建模
用法
跨域查询
用于原生 XML 支持的目录视图
简介
Microsoft SQL Server 2005 为 XML 数据处理提供了广泛的支持。XML 值可以自然地存储在 XML 数据类型列中,而后者可以根据 XML 架构集合进行类型化,或者保持非类型化。可以将 XML 列编入索引。而且,使用 XQuery 和 XML DML(为进行数据修改而进行的扩展)可以支持细粒度的数据操作。
SQL Server 2000 和 SQLXML Web Release 提供了强大的 XML 数据管理功能。这些功能致力于关系数据和 XML 数据之间的映射。可以使用带有批注的 XSD (AXSD) 来定义关系数据的 XML 视图,以便提供以 XML 为中心的方法,该方法支持 XML 数据的批量数据加载、查询和更新功能。Transact-SQL 扩展提供了以 SQL 为中心的方法,以便将关系查询结果映射到 XML(使用 FOR XML),以及从 XML 生成关系视图(使用 OpenXML)。这些支持已在 SQL Server 2005 中得到了扩展。结合新增的原生 XML 支持,SQL Server 2005 提供了一种强大的平台,以便针对半结构化和非结构化的数据管理开发功能丰富的应用程序。
本文提供了 SQL Server 2005 中的 XML 数据建模和使用准则。它包含以下两个主题:
数据建模 **
XML 数据可用多种方式存储在 SQL Server 2005 中,例如,使用原生 XML 数据类型和分散到表中的 XML。本主题提供了做出适当的选择以便对 XML 数据进行建模的准则。同时,还讨论了将 XML 数据编入索引、属性提升和 XML 实例的类型化。
用法 **
本主题讨论了与用法相关的主题(如将 XML 数据加载到服务器以及查询编译中的类型推理),解释和区分了密切相关的功能,并推荐了这些功能的适当使用。文中通过示例阐述了各种概念。
为了最大限度地领会本文的内容,您应该对 SQL Server 环境中的 XML 功能有一个基本的了解。请参阅 XML Support in Microsoft SQL Server 2005。
数据建模
本节概述了使用 SQL Server 2005 中的 XML 的理由,提供了在原生 XML 存储和 XML 视图技术之间进行选择的准则,并且提供了数据建模建议。
关系或 XML 数据模型
如果您的数据是高度结构化的,具有已知的架构,则关系模型可能对于数据存储最为有效。Microsoft SQL Server 提供了您可能需要的必要功能和工具。另一方面,如果结构是灵活的(半结构化和非结构化)或未知的,则必须适当地考虑如何对此类数据进行建模。
如果您需要独立于平台的模型,以便确保使用结构化和语义标记的数据的可移植性,则 XML 是一种不错的选择。而且,如果满足下列某些属性,则它还是一种适当的选择:
您的数据比较稀疏,或者您不了解数据的结构,或者数据的结构将来可能发生重大更改。
您的数据表示容器层次结构(与实体中的引用相对),并且可能是递归的。
您的数据具有内在的顺序。
您希望对数据进行查询,或者基于其结构更新部分数据。
如果上述任一条件都不满足,则您应该使用关系数据模型。例如,如果您的数据是 XML 格式,但您的应用程序很少使用数据库来存储和检索数据,则 [n]varchar(max) 列就能满足您的全部需要。在 XML 列中存储数据可以带来其他好处 - 引擎将检查数据格式规范或者有效,并且支持对 XML 数据进行细粒度的查询和更新。
在 SQL Server 2005 中存储 XML 数据的理由
以下为一些使用 SQL Server 2005 中的原生 XML 功能而不是在文件系统中管理 XML 数据的理由:
您希望使用数据库服务器的管理功能来管理 XML 数据(例如,备份、恢复和复制)。
您希望以高效的方式和事务处理方式来共享、查询和修改 XML数据。细粒度的数据访问对于您的应用程序而言很重要。例如,您可能需要提取 XML 文档内部的某些节,或者您可能需要插入一个新节而不是替换整个文档。
您具有关系数据和 SQL 应用程序,您希望在应用程序内部的关系数据和 XML 数据之间进行互操作。对于跨域应用程序,您需要有关查询和数据修改的语言支持。
您希望服务器能够保证数据格式规范,并能够视情况根据 XML 架构来验证数据。
您需要将 XML 数据编入索引以便实现高效的查询处理和良好的可伸缩性,并且使用一流的查询优化器。
您希望对 XML 数据进行 SOAP、ADO.NET 和 OLE DB 访问。
如果不满足上述任一条件,您最好将数据存储为非 XML 的大型数据类型,如 [n]varchar(max) 或 varbinary(max)。
XML 存储选项
SQL Server 2005 中的 XML 的存储选项如下所示:
本机存储采用 XML 数据类型:
用能够保留数据的 XML 内容(如容器层次结构、文档顺序、元素和属性值等等)的内部表示形式存储数据。具体说来,就是保留 XML 数据的信息集内容(有关信息集的详细信息,请参阅 http://www.w3.org/TR/xml-infoset)。它可能不是文本 XML 的精确副本,因为未保留以下信息:无关紧要的空格、属性顺序、命名空间前缀和 XML 声明。
对于类型化的 XML 数据类型(即绑定到 XML 架构的 XML 数据类型)而言,负责向信息集添加类型信息的后架构验证信息集 (Post Schema Validation Infoset, PSVI) 以内部表示形式编码。这会显著提高分析速度。(有关详细信息,请参阅 W3C XML 架构规范,网址为 http://www.w3.org/TR/xmlschema-1 和 http://www.w3.org/TR/xmlschema-2。)
XML 和关系存储之间的映射:
使用带有批注的架构 (AXSD),XML 将被分解到一个或多个表中的列,并且在关系级别保留数据的保真度 - 保留层次结构,但忽略元素顺序。架构不能是递归的。
大型对象存储([n]varchar(max) 和 varbinary(max)):
存储了数据的精确副本。这对于特殊用途的应用(如法律文档)很有用。大多数应用不要求精确副本,XML 内容(信息集保真度)即可满足需要。
通常情况下,可能需要组合使用这些方法。例如,您可能需要用 XML 数据类型列存储 XML 数据,并将其中的属性提升到关系列中。相反,您可能希望使用映射技术,将非递归部分存储到非 XML 列中,而仅将递归部分存储到 XML 数据类型列中。
XML 技术的选择
XML 技术(原生 XML 与 XML 视图)的选择通常取决于下列因素:
存储选项:
您的 XML 数据可能更适合于大型对象存储(例如,产品手册),或者更适合于存储在关系列中(例如,转换到 XML 的行项目)。每个存储选项都在不同程度上保留了文档保真度。
查询功能:
基于查询的性质以及对 XML 数据进行查询的程度,您可能发现一个存储选项比其他存储选项更为适合。细粒度的 XML 数据查询(例如,XML 节点上的谓词计算)在这两个存储选项中受到不同程度的支持。
将 XML 数据编入索引:
您可能希望将 XML 数据编入索引,以便提高 XML 查询性能。索引选项随存储选项的不同而不同;您需要进行适当的选择以优化工作量。
数据修改功能:
某些工作量涉及到对 XML 数据进行细粒度的修改(例如,在文档内添加新节),而其他工作量则不涉及(例如,Web 内容)。对于您的应用程序而言,数据修改语言支持可能很重要。
架构支持:
您的 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 文档(例如,已签署的文档、法律文档或股票交易订单)的精确文本副本。您可能需要将您的文档存储在 [n]varchar(max) 列中。
对于查询,可在运行时将数据转换为 XML 数据类型,然后对其执行 Xquery。运行时转换可能代价高昂,尤其是在文档很大时。如果您经常进行查询,可以采用冗余方式将文档存储在 XML 数据类型列中并将其编入索引,同时从 [n]varchar(max) 列返回精确的文档副本。
XML 列可能是基于 [n]varchar(max) 列的计算列。您不能在 XML 计算列上创建 XML 索引,也不能在 [n]varchar(max) 或 varbinary(max) 列上生成 XML 索引。
XML 视图技术
通过在 XML 架构和数据库的表之间定义映射,可以创建持久性数据的"XML 视图"。可以使用 XML 批量负载来填充使用 XML 视图的基础表。您可以查询使用 XPath 1.0 的 XML 视图;该查询将被转换为针对表的 SQL 查询。与此类似,更新也会被传递到这些表。
在以下情况下,此技术很有用:
您希望拥有以 XML 为中心的编程模型,该模型使用现有关系数据上的 XML 视图。
您的 XML 数据具有架构 (XSD, XDR),它可能由外部合作伙伴提供。
数据的顺序不重要,或者您的可查询数据不是递归的,或者预先已经知道最大递归深度。
您希望通过使用 XPath 1.0 的 XML 视图来查询和修改数据。
您希望批量加载 XML 数据,并将其分解到使用 XML 视图的基础表中。
这方面的例子包括以 XML 形式公开以便用于数据交换和 Web 服务的关系数据,以及具有固定架构的 XML 数据。有关详细信息,请参阅 SQLXML 开发人员中心。
示例:使用带有批注的 XML 架构 (AXSD) 对数据进行建模
假设您现有一些希望以 XML 形式进行操作的关系数据(例如,客户、订单和行项目)。请使用 AXSD 在关系数据上定义 XML 视图。通过 XML 视图,可以将 XML 数据批量加载到表中,以及使用 XML 视图查询和更新关系数据。如果您需要在自己的 SQL 应用程序持续工作时与其他应用程序中的 XML 标记交换数据,则该模式很有用。
混合模型
很多时候,适合将关系数据和 XML 数据类型列结合起来进行数据建模。可以将 XML 数据中的某些值存储在关系列中,而将其余或全部 XML 值存储在 XML 列中。这可能会产生更好的性能(例如,可以完全控制在关系列上创建的索引)和锁定特性。然而,这需要您承担更多的责任来管理数据存储。
要存储在关系列中的值取决于您的工作负荷。例如,如果您基于路径表达式 /Customer/@CustId 检索全部 XML 值,则通过将 CustId 属性的值提升到关系列中以及将其编入索引,可能产生更高的查询性能。另一方面,如果您的 XML 数据被广泛且非冗余地分解到关系列中,则重新组合的成本可能很大。
对于高度结构化的 XML 数据(例如,表的内容已经转换到 XML),可以将所有值映射到关系列(可能使用 XML 视图技术)。
使用 XML 数据类型进行数据建模
本节讨论有关原生 XML 存储的数据建模主题。这些主题包括将 XML 数据编入索引、属性提升和类型化 XML 数据类型。
相同或不同的表
XML 数据类型列可以在包含其他关系列的表中创建,也可以在与主表之间具有外键关系的独立表中创建。
在满足下列某个条件时,请在同一个表中创建 XML 数据类型列:
您的应用程序在 XML 列上执行数据检索,并且不需要 XML 列上的 XML 索引。 或者
您需要在 XML 数据类型列上生成 XML 索引,并且主表的主键与其聚集键相同。有关详细信息,请参阅将 XML 数据类型列编入索引一节。
在满足下列条件时,请在单独的表中创建 XML 数据类型列:
您需要在 XML 数据类型列上生成 XML 索引,但主表的主键与其聚集键不同,或者主表不具有主键,或者主表是一个堆(也就是说,没有聚集键)。如果主表已经存在,则这可能是真的。
您不希望表扫描由于表中存在 XML 列(无论它是存储在行中还是存储在行外,都会占用空间)而降低速度。
XML 数据的粒度
XML 列中存储的 XML 数据的粒度对于锁定和更新特性而言至关重要。SQL Server 对 XML 和非 XML 数据采用了相同的锁定机制。因此,行级锁定会导致相应行中的所有 XML 实例被锁定。当粒度比较大时,锁定大型 XML 实例以便进行更新会导致多用户场合下的吞吐量下降。另一方面,严重的分解会丢失对象封装并提高重新组合成本。
对 XML 实例进行更新会将现有实例替换为更新后的实例,即使只修改单个属性的值。XML 数据粒度越大,更新成本就越高。较小的 XML 实例会产生较高的更新性能。
对于良好的设计而言,重要的是保持数据建模需要与锁定和更新特性之间的平衡。
非类型化、类型化和受约束的 XML 数据类型
SQL Server 2005 XML 数据类型实现了 ISO SQL-2003 标准 XML 数据类型。因此,它可以在非类型化 XML 列中存储格式规范的 XML 1.0 文档以及带有文本节点和任意数量顶级元素的所谓的 XML 内容片段。系统将检查数据的格式是否规范,不要求将列绑定到 XML 架构,并且拒绝在扩展意义上格式不规范的数据。对于非类型化 XML 变量和参数,也是如此。
如果您具有描述 XML 数据的 XML 架构,则可以将这些架构与 XML 列相关联,以便产生类型化 XML。XML 架构用于对数据进行有效性验证,在查询和数据修改语句编译过程中执行比非类型化 XML 更准确的类型检查,以及优化存储和查询处理。
在下列条件下,请使用非类型化 XML 数据类型:
您没有对应于 XML 数据的架构
您拥有架构,但不希望服务器对数据进行有效性验证。当应用程序在服务器中存储数据之前执行客户端验证时,或者暂时存储根据架构无效的 XML 数据时,或者使用在服务器中不受支持的架构组件(例如 key/keyref)时,有时会出现这种情况。
在下列条件下,请使用类型化 XML 数据类型:
您拥有对应于 XML 数据的架构,并且希望服务器按照 XML 架构对 XML 数据进行有效性验证。
您希望充分利用基于类型信息的存储和查询优化。
您希望在查询的编译过程中更充分地利用类型信息。
类型化 XML 列、参数和变量可以存储 XML 文档或内容 - 您在声明时必须将它们指定为标志(分别指定为 DOCUMENT 或 CONTENT)。而且,您必须提供 XML 架构集合。如果每个 XML 实例恰好有一个顶级元素,请指定 DOCUMENT;否则,请使用 CONTENT。查询编译器在查询编译过程的类型检查中使用 DOCUMENT 标记来推理唯一的顶级元素。
除了将 XML 列类型化以外,您还可以在类型化或非类型化 XML 数据类型列上使用关系(列或行)约束。在下列条件下,请使用约束:
无法在 XML 架构中表示业务规则。例如,花店的送货地址必须在其营业地点周围 50 英里范围之内,这可以编写为 XML 列上的约束。该约束可能涉及到 XML 数据类型方法。
您的约束涉及到表中的其他 XML 列或非 XML 列。这方面的一个例子是:强制 XML 实例中存在的 Customer ID (/Customer/@CustId) 与关系 CustomerID 列中的值匹配。
文档类型定义 (DTD)
XML 数据类型列、变量和参数可以使用 XML 架构而不是 DTD 加以类型化。然而,对于非类型化和类型化 XML,都可以使用内联 DTD 来提供默认值,以便将实体引用替换为它们的扩展形式。
您可以使用第三方工具将 DTD 转化为 XML 架构文档,并且将 XML 架构加载到数据库中。
将 XML 数据类型列编入索引
可以在 XML 数据类型列上创建 XML 索引。这会将该列中 XML 实例上的所有标记、值和路径编入索引,从而提高查询性能。在下列条件下,您的应用程序可能受益于 XML 索引:
对 XML 列进行查询在您的工作负荷中很常见。必须考虑数据修改过程中的 XML 索引维护成本。
XML 值相对较大,而检索的部分相对较小。生成索引可以避免在运行时分析全部数据,并且因为受益于索引查找而提高查询处理的性能。
XML 列上的第一个索引是"主 XML 索引"。通过该索引,可以在 XML 列上创建三种类型的辅助 XML 索引,从而提高常见种类的查询的速度,如下节所述。
主 XML 索引
这会将 XML 列中的 XML 实例内部的所有标记、值和路径编入索引。基表(即包含 XML 列的表)必须在该表的主键上具有聚集索引;主键用于将索引行与基表中的行相关联。从 XML 列中检索完整的 XML 实例(例如 SELECT *)。查询使用主 XML 索引,并返回标量值或使用索引本身的 XML 子树。
示例:创建主 XML 索引
在我们的多数示例中,都使用带有非类型化 XML 列的表 T (pk INT PRIMARY KEY, xCol XML),这些示例都可以简单地扩展为类型化 XML 的形式(有关使用类型化 XML 的信息,请参阅 SQL Server 2005 联机图书)。为了便于说明,将针对如下所示的 XML 数据实例描述查询:
<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>
下面的语句在表 T 的 XML 列 xCol 上创建了名为 idx_xCol 的 XML 索引:
CREATE PRIMARY XML INDEX idx_xCol on T (xCol)
辅助 XML 索引
在创建主 XML 索引之后,您可能希望创建辅助 XML 索引来提高工作负荷中的不同种类查询的速度。三种类型的辅助 XML 索引 - PATH、PROPERTY 和 VALUE 分别为基于路径的查询、自定义属性管理场合和基于值的查询提供帮助。PATH 索引在列中的所有 XML 实例上,按照文档顺序生成各个 XML 节点的 (path, value) 对的 B+ 树。PROPERTY 索引创建各个 XML 实例中 (PK, path, value) 对的聚集 B+ 树,其中 PK 是基表的主键。最后,VALUE 索引在 XML 列中的所有 XML 实例中,按照文档顺序创建各个节点的 (value, path) 对的 B+ 树。
以下是创建上述一个或多个索引的一些准则:
如果工作负荷大量使用 XML 列中的路径表达式,则 PATH 辅助 XML 索引可能会加快工作负荷的处理速度。最常见的例子是在 T-SQL 的 WHERE 子句中对 XML 列使用 exist() 方法。
如果您的工作负荷从单独的使用路径表达式的 XML 实例中检索多个值,则将各个 XML 实例中的路径聚集到 PROPERTY 索引中可能会很有用。这种情况通常出现在属性包场合中,此时对象的属性被获取并且其主键值已知。
如果您的工作负荷涉及到查询 XML 实例中的值,而不知道包含这些值的元素或属性名称,则您可能需要创建 VALUE 索引。这通常发生在子代轴查找中,例如 //author[last-name="Howard"],其中 元素可以出现在层次结构的任意级别上。这种情况还会发生在"通配符"查询中,例如 /book [@* = "novel"],其中查询将查找具有某个值为 "novel" 的属性的 元素。
示例:基于路径的查找
假设下面的查询在您的工作负荷中很常见:
SELECT pk, xCol FROM T WHERE xCol.exist ('/book[@genre = "novel"]') = 1
路径表达式 /book/@genre 和值 "novel" 对应于 PATH 索引的键字段。因此,PATH 类型的辅助 XML 索引对于该工作负荷很有用:
CREATE XML INDEX idx_xCol_Path on T (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 T
在这种情况下,属性索引很有用,其创建方式如下所示:
CREATE XML INDEX idx_xCol_Property on T (xCol) USING XML INDEX idx_xCol FOR PROPERTY
示例:基于值的查询
在以下查询中,子代轴或自身轴 (//) 指定了部分路径,以便基于 ISBN 值的查找可以因为使用 VALUE 索引而受益:
SELECT xCol FROM T WHERE xCol.exist ('//book[@ISBN = "1-8610-0157-6"]') = 1
VALUE 索引按如下方式创建:
CREATE XML INDEX idx_xCol_Value on T (xCol) USING XML INDEX idx_xCol FOR VALUE
XML 列上的全文索引
您可以在 XML 列上创建全文索引,从而将 XML 值的内容编入索引,而忽略 XML 标记。属性值没有被编入全文索引(因为它们被视为标记的一部分),并且元素标记被用作标记边界。在某些情况下,可以将全文搜索与 XML 索引用法结合起来:
首先,使用 SQL 全文搜索筛选感兴趣的 XML 值。
接下来,查询这些 XML 值,这会使用 XML 列上的 XML 索引。
示例:将全文搜索与 XML 查询结合起来
在 XML 列上创建全文索引之后,以下查询将检查 XML 值是否在书名中包含单词"custom":
SELECT * FROM T WHERE CONTAINS(xCol,'custom') AND xCol.exist('/book/title/text()[contains(.,"custom")]') =1
CONTAINS() 方法使用全文索引,将文档中任何地方包含单词"custom"的 XML 值组合为一个子集。exist() 子句确保单词"custom"出现在书名中。
使用 CONTAINS() 和 XQuery contains() 的全文搜索具有不同的语义。后者是子字符串匹配,而前者则是使用单词衍生的标记匹配。因此,如果要搜索标题中的字符串 "run",则 "run"、"runs" 和 "running" 都将匹配,因为全文 CONTAINS() 和 Xquery contains() 都满足。然而,上述查询不匹配标题中的单词"customizable"。(全文 CONTAINS() 失败,而 Xquery contains() 被满足)。通常,对于纯粹的子字符串匹配,应该删除全文 CONTAINS() 子句。
而且,全文搜索采用单词衍生,而 XQuery contains() 是一种字面匹配。这一区别将在下一个示例中阐述。
示例:使用单词衍生对 XML 值进行全文搜索
通常情况下,不能排除示例:将全文搜索与 XML 查询结合起来中的 XQuery contains() 检查。请考虑查询:
SELECT * FROM T WHERE CONTAINS(xCol,'run')
因为使用单词衍生,所以文档中的单词"ran"匹配搜索条件。而且,使用 XQuery 时不会检查搜索上下文。
在使用被全文索引的 AXSD 将 XML 分解到关系列中时,XML 视图上的 XPath 查询不会对基础表执行全文搜索。
属性提升
如果主要是对少量元素和属性值进行查询(例如,基于客户 ID 查找客户,即指定了 /Customer/@CustId 的值),您可能希望将这些数量提升到关系列中。当检索了整个 XML 实例,但只对一小部分 XML 数据进行查询时,这将很有用。在 XML 列上创建 XML 索引是没有必要的;相反,可以将被提升的列编入索引。必须编写查询以使用提升的列(即,查询优化器不会将对 XML 列的查询重新定向到提升的列)。
提升的列可以是同一表中的计算列,也可以是表中单独的、用户维护的列。当从各个 XML 实例中提升唯一值(即单值属性)时,这已足够。然而,对于多值属性,您必须为该属性创建单独的表,如下所述。
基于 XML 数据类型的计算列
可以使用能够激活 XML 数据类型方法的用户定义函数 (UDF) 来创建计算列。计算列的类型可以是任何 SQL 类型,包括 XML。以下示例说明了这一点。
示例:基于 XML 数据类型方法的计算列
为书籍的 ISBN 创建用户定义的函数:
CREATE FUNCTION udf_get_book_ISBN (@xData xml) RETURNS varchar(20) BEGIN DECLARE @ISBN varchar(20) SELECT @ISBN = @xData.value('/book[1]/@ISBN', 'varchar(20)') RETURN @ISBN END
为 ISBN 向表中添加一个计算列:
ALTER TABLE T ADD ISBN AS dbo.udf_get_book_ISBN(xCol)
可以用通常的方式将计算列编入索引。
示例:基于 XML 数据类型方法的计算列上的查询
要获取其 ISBN 为 0-7356-1588-2 的 ,可以改写 XML 列上的查询
SELECT xCol FROM T WHERE xCol.exist ('/book[@ISBN = "0-7356-1588-2"]') = 1
以使用计算列,如下所示:
SELECT xCol FROM T WHERE ISBN = '0-7356-1588-2'
可以创建一个用户定义的函数,返回 XML 数据类型和使用该 UDF 的计算列。然而,无法在计算的 XML 列上创建 XML 索引。
创建属性表
您可能希望将 XML 数据中的某些多值属性提升到一个或多个表中,在这些表上创建索引,并且重定向查询以使用这些表。典型的情形是一小部分属性覆盖了大部分查询工作负荷。您可以执行以下操作:
创建一个或多个表以存放多值属性。您可能发现采用以下处理方式会很方便:每个表存储一个属性,并且在属性表中复制基表的主键以便与基表进行向后联接。
如果您希望保持属性的相对顺序,则需要为相对顺序引入一个单独的列。
在 XML 列上创建触发器以便维护属性表。在触发器中,执行以下操作:
使用 XML 数据类型方法(如 nodes() 和 value())在属性表中插入和删除行。(有关 nodes() 方法的详细信息,请参阅 Value()、Nodes() 和 OpenXML()。)
在 CLR 中创建流式表值函数,以便在属性表中插入和删除行。
编写查询,以便对属性表进行 SQL 访问,以及对基表中的 XML 列进行 XML 访问,这需要使用这些表的主键将其相互联接。
示例:创建属性表
假设您希望提升作者的名字。书籍有一个或多个作者,因此名字是一个多值属性。每个名字都存储在属性表的单独行中。在属性表中复制了基表的主键以便向后联接。
create table tblPropAuthor (propPK int, propAuthor varchar(max))
示例:创建用户定义的函数以便从 XML 实例生成行集
下面的表值函数 udf_XML2Table 接受一个主键值和一个 XML 实例。它将检索 元素的所有作者的名字,并返回(主键,名字)对行集。
create function udf_XML2Table (@pk int, @xCol xml) returns @ret_Table table (propPK int, propAuthor varchar(max)) with schemabinding as begin insert into @ret_Table select @pk, nref.value('.', 'varchar(max)') from @xCol.nodes('/book/author/first-name') R(nref) return end
示例:创建触发器以填充属性表
插入触发器:在属性表中插入行
create trigger trg_docs_INS on T for insert as declare @wantedXML xml declare @FK int select @wantedXML = xCol from inserted select @FK = PK from inserted insert into tblPropAuthor select * from dbo.udf_XML2Table(@FK, @wantedXML)
删除触发器:基于删除行的主键值,从属性表中删除行
create trigger trg_docs_DEL on T for delete as declare @FK int select @FK = PK from deleted delete tblPropAuthor where propPK = @FK
更新触发器:在与更新的 XML 实例对应的属性表中删除现有行,并且在该属性表中插入新行
create trigger trg_docs_UPD on T for update as if update(xCol) or update(pk) begin declare @FK int declare @wantedXML xml select @FK = PK from deleted delete tblPropAuthor where propPK = @FK select @wantedXML = xCol from inserted select @FK = pk from inserted insert into tblPropAuthor select * from dbo.udf_XML2Table(@FK, @wantedXML) end
示例:查找作者的名字为"David"的 XML 实例
可以在 XML 列上表示该查询。另外,还可以在属性表中搜索名字"David",然后与基表执行向后联接以返回 XML 实例,如下所示:
SELECT xCol FROM T JOIN tblPropAuthor ON T.pk = tblPropAuthor.propPK WHERE tblPropAuthor.propAuthor = 'David'
示例:使用 CLR 流式表值函数的解决方案
该解决方案包括以下步骤:(a) 定义 CLR 类 SqlReaderBase,它实现了 ISqlReader,并且通过在 XML 实例上应用路径表达式来生成流式表值输出;(b) 创建一个程序集和一个 T-SQL 用户定义函数 (UDF) 来激活该 CLR 类;(c) 使用 UDF 定义插入、更新和删除触发器,以维护属性表。
首先,创建流式 CLR 函数,其主干如下所示。XML 数据类型被公开为 ADO.NET 中的托管类 SqlXml;它支持返回 XmlReader 的方法 CreateReader()。
public class c_streaming_xml_tvf { public static ISqlReader streaming_xml_tvf (SqlXml xmlDoc, string pathExpression) { return (new TestSqlReaderBase (xmlDoc, pathExpression)); } } // Class that implements ISqlReader public class TestSqlReaderBase : ISqlReader { XPathNodeIterator m_iterator; ???public SqlChars FirstName; // Metadata for current resultset private SqlMetaData[] m_rgSqlMetaData; public TestSqlReaderBase (SqlXml xmlDoc, string pathExpression) { // Variables for XPath navigation XPathDocument xDoc; XPathNavigator xNav; XPathExpression xPath; // Set sql meta data m_rgSqlMetaData = new SqlMetaData[1]; m_rgSqlMetaData[0] = new SqlMetaData ("FirstName", SqlDbType.NVarChar,50); //Set up the Navigator if (!xmlDoc.IsNull) xDoc = new XPathDocument (xmlDoc.CreateReader()); else xDoc = new XPathDocument (); xNav = xDoc.CreateNavigator(); xPath = xNav.Compile (pathExpression); m_iterator = xNav.Select(xPath); } public bool Read() { bool moreRows = true; if (moreRows = m_iterator.MoveNext()) ???FirstName = new SqlChars (m_iterator.Current.Value); return moreRows; } }
接下来,创建一个程序集,以及一个与 CLR 函数 streaming_xml_tvf 对应的 T-SQL 用户定义函数 SQL_streaming_xml_tvf(未显示)。该 UDF 用于定义表值函数 CLR_udf_XML2Table 以便生成行集:
create function CLR_udf_XML2Table (@pk int, @xCol xml) returns @ret_Table table (FK int, FirstName varchar(max)) with schemabinding as begin insert into @ret_Table select @pk, FirstName FROM SQL_streaming_xml_tvf (@xCol, '/book/author/first-name') return end
最后,定义触发器,如示例创建触发器以填充属性表中所示,但用函数 CLR_udf_XML2Table 替换 udf_XML2Table。因此,插入触发器将如下所示:
create trigger CLR_trg_docs_INS on T for insert as declare @wantedXML xml declare @FK int select @wantedXML = xCol from inserted select @FK = PK from inserted insert into tblPropAuthor select * from dbo.CLR_udf_XML2Table(@FK, @wantedXML)
删除触发器与非 CLR 版本完全相同,而更新触发器只是将函数 udf_XML2Table() 替换为 CLR_udf_XML2Table()。
XML 架构集合
XML 架构集合是一个元数据实体,其范围由关系架构确定,包含一个或多个可能相关(例如,通过 )或无关的 XML 架构。XML 架构集合中的单独 XML 架构由其目标命名空间标识。
XML 架构集合是使用 CREATE XML SCHEMA COLLECTION 语法创建的,并且提供了一个或多个 XML 架构。可以向现有 XML 架构中添加更多的 XML 架构组件,并且可以使用 ALTER XML SCHEMA COLLECTION 语法向 XML 架构集合中添加更多的架构。可以使用 SQL Server 2005 中的安全模型像任何 SQL 对象那样保证 XML 架构集合的安全。
多类型化列
XML 架构集合 C 按照多个 XML 架构将 XML 列 xCol 类型化。此外,标志 DOCUMENT 或 CONTENT 分别指定 XML 树或片段是否可以存储在列 xCol 中。
对于 DOCUMENT,每个 XML 实例都会按照用来对其进行验证和类型化的命名空间,指定实例中顶级元素的目标命名空间。另一方面,对于 CONTENT,每个顶级元素都可以指定 C 中的任一目标命名空间。XML 实例将按照实例中存在的所有目标命名空间进行验证和类型化。
架构演变
XML 架构集合用于类型化 XML 列、变量和参数。它提供了一种 XML 架构演变机制。假设您将带有目标命名空间 BOOK-V1 的 XML 架构添加到 XML 架构集合 C 中。使用 C 加以类型化的 XML 列 xCol 可以存储符合 BOOK-V1 架构的 XML 数据。
假设某个应用程序希望通过新的架构组件(例如复杂类型定义和顶级元素声明)来扩展 XML 架构。这些新的架构组件可以添加到 BOOK-V1 架构中,并且不要求对列 xCol 中的现有 XML 数据进行重新验证。
假设该应用程序后来希望提供该 XML 架构的新版本,并且为该新版本选择目标命名空间 BOOK-V2。该 XML 架构可以添加到 C 中。XML 列可以存储 BOOK-V1 和 BOOK-V2 二者的实例,并且对符合这些命名空间的 XML 实例执行查询和数据修改。
用法
加载 XML 数据
将 XML 数据从 SQL Server 2000 传输到 SQL Server 2005
可以用多种方式将 XML 数据传输到 SQL Server 2005。在下一节中,我们将讨论几种方案。
如果您将数据存储在 SQL Server 2000 数据库的 [n]text 或图像列中,可以使用 DTS 等将表导入到 SQL Server 2005 数据库中。使用 ALTER TABLE 语句将列类型更改为 XML。
可以使用 bcp out 批量复制 SQL Server 2000 中的数据,使用 bcp in 将数据批量插入到 SQL Server 2005 数据库中。
如果您将数据存储在 SQL Server 2000 数据库的关系列中,请创建一个带有一个 ntext 列的新表,同时根据需要在该表中创建一个主键列以用作行标识符。使用客户端编程检索在服务器中通过 FOR XML 生成的 XML,并且将其写入 ntext 列。然后,使用上述技巧将数据传输到 SQL Server 2005 数据库。您可以选择将 XML 直接写入 SQL Server 2005 数据库的 XML 列中。
示例:将列类型更改为 XML
假设您需要将表 R 中的 [n]text 或图像列 XYZ 的类型更改为非类型化 XML。下面的语句可执行此类更改:
ALTER TABLE R ALTER COLUMN XYZ XML
- 如果需要,可以通过指定一个 XML 架构集合将目标类型化为 XML。
批量加载 XML 数据
可以使用 SQL Server 中的批量加载功能(如 bcp),将 XML 数据批量加载到服务器中。通过 OPENROWSET 可以将文件中的数据加载到 XML 列中。下面的示例阐明了这一点。
示例:从文件中加载 XML
该示例说明了如何在表 T 中插入行。XML 列的值作为 CLOB 从文件 C:\yukon\xmlfile.xml 中加载,并且整数列被提供了值 10。
INSERT INTO T SELECT 10, xCol FROM (SELECT * FROM OPENROWSET (BULK 'C:\Yukon\xmlfile.xml', SINGLE_CLOB) AS xCol) AS R(xCol)
文本编码
SQL Server 2005 用 Unicode (UTF-16) 存储 XML 数据。从服务器中检索的 XML 数据采用 UTF-16 编码;如果您需要不同的编码,则需要对检索到的数据执行必要的转换。有时,您可能拥有采用不同编码的 XML 数据,因此在数据加载过程中需要非常小心:
如果文本 XML 采用 Unicode (UCS-2, UTF-16),则将其赋给 XML 列、变量或参数不会带来任何问题。
如果编码不是 Unicode 并且是隐式的(由于源代码页),则数据库中的字符串代码页应该与要加载的代码点相同或兼容(必要时使用 COLLATE)。如果不存在这样的服务器代码页,则您必须添加带有正确编码的显式 XML 声明。
要使用显式编码,请使用 varbinary() 类型(它不与代码页交互)或者使用适当代码页的字符串类型。然后,将该数据赋给 XML 列、变量或参数。
示例:显式指定编码
假设您具有的 XML 文档 (vcdoc) 被存储为没有显式 XML 声明的 varchar(max)。下面的语句将添加一个带有编码"iso8859-1"的 XML 声明,将 XML 文档串连起来,将结果转换为 varbinary(max) 以便保留字节表示形式,最后将其转换为 XML。这使 XML 处理器能够按照指定的编码"iso8859-1"来分析数据,并为字符串值生成相应的 UTF-16 表示形式。
SELECT CAST( CAST (('<?xml version="1.0" encoding="iso8859-1"?>'+ vcdoc) AS VARBINARY (MAX)) AS XML)
Xquery 与类型推理
嵌入到 T-SQL 中的 XQuery (http://www.w3.org/TR/xquery/) 语言支持查询 XML 数据类型。该语言正在由 WWW 联合会 (W3C) 进行开发(在本文作者最后一次召集起来撰写本文时),并且所有主要数据库供应商(包括 Microsoft)都参与了开发工作。它包括了 XPath 2.0 作为导航语言。同时,还提供了针对 XML 数据类型的数据修改语言构造。有关 SQL Server 2005 中支持的 Xquery 构造、函数和运算符的信息,请参阅联机图书。
错误模型
具有语法错误的 Xquery 表达式和 XML DML 语句会返回编译错误。编译阶段会检查 XQuery 表达式和 DML 语句的静态类型正确性,并且对于类型化 XML 使用 XML 架构进行类型推理。如果某个表达式可能在运行时由于类型安全冲突而失败,它会引发静态类型错误。静态错误的例子有将字符串添加到整数以及在不存在的节点中查询类型化数据。
与 W3C 标准有所不同的是,XQuery 运行时错误被转换为可以作为空 XML 或 NULL 传播给查询结果的空序列(具体取决于调用上下文)。
通过显式转换到正确的类型,用户可以避免静态错误,尽管运行时转换错误将被转化为空序列。
下面的小节将详细讨论类型检查。
唯一性检查
如果编译器无法确定能否在运行时保证唯一性,则要求唯一性的定位步骤、函数参数和运算符(例如 eq)将返回错误。问题经常出现在非类型化数据上。例如,属性查找要求存在唯一的父元素;能够选择单个父节点的序号即可满足需要。计算 node()-value() 组合(请参阅 Value()、Nodes() 和 OpenXML())以提取属性值,这可能不需要指定序号,如下面的示例所示。
示例:已知的唯一性
在该示例中,nodes() 方法为每个 元素生成一个单独的行。(有关 nodes() 方法的详细说明,请参阅 Value()、Nodes() 和 OpenXML())。在 节点上进行求值的 value() 方法会提取 @genre(它作为属性具有唯一性)的值。
SELECT nref.value('@genre', 'varchar(max)') LastName FROM T CROSS APPLY xCol.nodes('//book') AS R(nref)
XML 架构用于对类型化 XML 进行类型检查。如果节点被指定为 XML 架构中的唯一节点,则编译器将使用该信息,并且不会出现任何错误。否则,需要能够选择单个节点的序号。特别地,如果使用子代或自身轴 (//),例如 /book//title,则会丢失 <title> 元素的唯一性基数推理,即使 XML 架构指定其具有这种性质。请将其改写为 (/book//title)[1]。
对于类型检查,需要记住 //first-name[1] 和 (//first-name)[1] 之间的差别。前者返回 节点的序列,其中每个节点是其同辈节点中最左边的 节点。后者返回 XML 实例中按照文档顺序的第一个唯一的 节点。
示例:value() 的用法
下面这个对非类型化 XML 列执行的查询会导致静态、编译错误,因为 value() 需要将一个唯一性节点作为第一个参数,但编译器无法确定在运行时是否将只出现一个 节点:
SELECT xCol.value('//author/last-name', 'nvarchar(50)') LastName FROM T
您可能会尝试以下解决办法:
SELECT xCol.value('//author/last-name[1]', 'nvarchar(50)') LastName FROM T
但是,这不会纠正该错误,因为在每个 XML 实例中都可能出现多个 节点。下面的改写方式将会有效:
SELECT xCol.value('(//author/last-name)[1]', 'nvarchar(50)') LastName FROM T This query returns the value of the first element in each XML instance.
父轴
如果节点的类型无法确定,则它将成为 anyType,后者不会隐式转换为任何其他类型。在使用父轴(例如,xCol.query('/book/@genre/../price'))进行导航的过程中,尤其会发生这种情况;该父节点类型被确定为 anyType。元素也可能被定义为 XML 架构中的 anyType。在这两种情况下,丢失更为精确的类型信息通常会导致静态类型错误,并且要求将原子值显式转换为它们的特定类型。
Data()、Text() 和 String() 访问器
XQuery 具有一个可从节点中提取标量的、类型化值的函数 fn:data(),一个可返回文本节点的节点测试 text(),以及可返回节点的字符串值的函数 fn:string()。它们的用法有时会引起混乱。下面是有关在 SQL Server 2005 中正确使用它们的准则。请考虑 XML 实例 12:
非类型化 XML:路径表达式 /age/text() 返回文本节点"12"。函数 fn:data(/age) 返回字符串值"12",fn:string(/age) 也是如此。
类型化 XML:对于任何简单的类型化 元素,表达式 /age/text() 都会返回静态错误。另一方面,fn:data(/age) 返回整数 12,而 fn:string(/age) 会产生字符串"12"。
联合类型的函数和运算符
由于类型检查,联合类型要求进行认真的处理。以下示例阐述了其中两个问题。
示例:联合类型上的函数
请考虑以下联合类型的 <r> 的元素定义
<xs:element name="r"> <xs:simpleType> <xs:union memberTypes="xs:int xs:float xs:double"/> </xs:simpleType> </xs:element>
在 XQuery 上下文中,"average"函数 fn:avg (//r) 会返回静态错误,因为 XQuery 编译器无法对 fn:avg() 的参数中 元素的不同类型(xs:int、xs:float 或 xs:double)的值求和。为解决该问题,请将函数调用改写为 fn:avg(for $r in //r return $r cast as xs:double ?)。
示例:联合类型上的运算符
加法运算"+"要求精确的操作数类型,以至于表达式 (//r)[1] + 1 对上述元素 的类型定义返回静态错误。可以解决该问题的一种改写方式是 (//r)[1] cast as xs:int?+1,其中"?"表示具体取值 0 或 1。SQL Server 2005 要求带有"?"的"cast as",因为任何转换都会由于运行时错误而产生空序列。
Value()、Nodes() 和 OpenXML()
可以在 SELECT 子句中对 XML 数据类型使用多个 value() 方法来生成提取值的行集。nodes() 方法会为所选的每个节点生成一个内部引用,以用于进一步查询。当行集具有多个列,并且用于生成行集的路径表达式可能比较复杂时,将 nodes() 和 value() 方法组合使用可能会更为有效。
nodes() 方法可生成特殊 XML 数据类型的实例,每个实例都将其上下文设置为所选的不同节点。此类 XML 实例支持 query()、value()、nodes() 和 exist() 方法,并且可用在 count(*) 聚合中。所有其他用法都会导致错误。
示例:nodes() 的用法
假设您希望提取名字不是"David"的作者的姓名,作为由两个列(FirstName 和 LastName)组成的行集。使用 nodes() 和 value() 方法可以达到此目的,如下所示:
SELECT nref.value('first-name[1]', 'nvarchar(50)') FirstName, nref.value('last-name[1]', 'nvarchar(50)') LastName FROM T CROSS APPLY xCol.nodes('//author') AS R(nref) WHERE nref.exist('.[first-name != "David"]') = 1
在该示例中,nodes('//author') 会生成一个由对每个 XML 实例的 元素的引用组成的行集。通过相对于这些引用对 value() 方法求值,可以获取作者的名字和姓氏。
SQL Server 2000 提供了使用 OpenXml() 从 XML 实例生成行集的功能。您可以指定行集的关系架构,并指定 XML 实例内部的值如何映射到该行集中的列。
示例:对 XML 数据类型使用 OpenXml()
我们可以像下面显示的那样,使用 OpenXml() 来改写上一示例中的查询,方法是:创建一个游标,将各个 XML 实例读入一个 XML 变量,然后向其应用 OpenXML():
DECLARE name_cursor CURSOR FOR SELECT xCol FROM T OPEN name_cursor DECLARE @xmlVal XML DECLARE @idoc int FETCH NEXT FROM name_cursor INTO @xmlVal WHILE (@@FETCH_STATUS = 0) BEGIN EXEC sp_xml_preparedocument @idoc OUTPUT, @xmlVal SELECT * FROM OPENXML (@idoc, '//author') WITH (FirstName varchar(50) 'first-name', LastName varchar(50) 'last-name') R WHERE R.FirstName != 'David' EXEC sp_xml_removedocument @idoc FETCH NEXT FROM name_cursor INTO @xmlVal END CLOSE name_cursor DEALLOCATE name_cursor
OpenXml() 会创建内存中的表示形式,并使用工作表而不是查询处理器。它依赖于 MSXML 3.0 的 XPath 1.0 处理器而不是 XQuery 引擎。工作表不在对 OpenXml() 的多个调用中共享(即使是在同一个 XML 实例上)。这限制了它的可伸缩性。在未指定 WITH 子句时,可以通过 OpenXml() 来访问 XML 数据的边缘表格式。而且,还可以通过它使用 XML 值在单独的"溢出"列中的剩余部分。
nodes() 和 value() 函数的组合可以有效地使用 XML 索引。因此,这一组合可以表现出比 OpenXml 更高的可伸缩性。
使用 FOR XML 从行集中生成 XML
通过新的 TYPE 指令,可以使用 FOR XML 从行集中生成 XML 数据类型实例。
可以将结果赋给 XML 数据类型列、变量或参数。而且,可以将 FOR XML 嵌套以便生成任意层次结构。这使得嵌套的 FOR XML 比 FOR XML EXPLICIT 更加便于编写,但是对于较深的层次结构,它的性能可能不太好。FOR XML 还引入了新的 PATH 模式,该模式指定列的值应该出现在 XML 树中的哪个路径。
可以使用新的 FOR XML TYPE 指令,通过 SQL 语法来定义关系数据上的只读 XML 视图。可以通过 SQL 语句和嵌入式 XQuery 来查询该视图,如下面的示例所示。例如,您可以在存储过程中引用此类 SQL 视图。
示例:返回生成的 XML 数据类型的 SQL 视图
下面的 SQL 视图定义可在一个关系列 (pk) 以及从一个 XML 列中检索到的书籍作者上创建一个 XML 视图:
CREATE VIEW V (xmlVal) AS SELECT pk, xCol.query('/book/author') FROM T FOR XML AUTO, TYPE
视图 V 包含一个行,该行只有一个列:XML 类型的 xmlValtype。可以像查询常规的 XML 数据类型实例那样查询它。例如,下面的查询将返回名字为"David"的作者:
SELECT xmlVal.query('//author[first-name = "David"]') FROM V
SQL 视图定义在某种程度上类似于使用带有批注的架构创建的 XML 视图。然而,二者之间存在重要的区别。SQL 视图定义是只读的,并且必须通过嵌入式 XQuery 来操作;而使用带有批注的架构的 XML 视图则不是这样。而且,SQL 视图在应用 XQuery 表达式之前生成 XML 结果,而 XML 视图上的 XPath 查询在基础表上计算 SQL 查询。
添加业务逻辑
可以用多种方式将业务逻辑添加到 XML 数据中:
您可以编写行或列约束,在插入和修改 XML 数据的过程中实施特定于域的约束。
您可以在 XML 列上编写相应的触发器,使其当您在该列中插入或更新值时引发。该触发器可以包含特定于域的验证规则,或者填充属性表。
可以使用托管代码编写 SQLCLR 函数并向其传递 XML 值,并且使用由 System.Xml 命名空间提供的 XML 处理功能。这方面的一个例子是将 XSL 转换应用于 XML 数据,如下所示。您还可以将 XML 反序列化为一个或多个托管类,并且使用托管代码来操作它们。
您可以编写 T-SQL 存储过程和函数,激活 XML 列上的处理以满足您的业务需要。
示例:应用 XSL 转换
考虑 CLR 函数 TransformXml(),它接受一个 XML 数据类型实例和一个存储在文件中的 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); }
在注册该程序集,并且创建了对应于 TransformXml() 的用户定义 T-SQL 函数 SqlXslTransform() 之后,就可以像在下面的查询中那样从 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 和 Microsoft Visual Studio"Whidbey"联机图书。
跨域查询
当您的数据同时驻留在关系列和 XML 数据类型列中时,您可能希望编写将关系数据处理和 XML 数据处理结合起来的查询。例如,您可以使用 FOR XML 将关系列和 XML 列中的数据转换为 XML 数据类型实例,然后使用 XQuery 对其进行查询。相反,可以从 XML 值生成行集(请参阅用法),并使用 T-SQL 对其进行查询。
编写跨域查询的更为方便和有效的方法是在 XQuery 或 XML DML 表达式内使用 SQL 变量或列的值:
可以在 XQuery 或 XML DML 表达式中,通过 sql:variable() 来使用 SQL 变量的值。
可以在 XQuery 或 XML DML 表达式中,通过 sql:column() 来使用关系列中的值。
该方法使应用程序可以将查询参数化,如下面的示例所示。然而,不允许在 sql:variable() 和 sql:column() 中使用 XML 和用户定义的类型。
示例:使用 sql:variable() 的跨域查询
下面的查询是对示例:基于 XML 数据类型方法的计算列上的查询中显示的查询进行修改后得到的版本。在该版本中,使用 SQL 变量 @isbn 传入感兴趣的 ISBN。通过将常量替换为 sql:variable(),可以使用该查询来搜索任意 ISBN,而不仅是其 ISBN 为 0-7356-1588-2 的那个。
DECLARE @isbn varchar(20) SET @isbn = '0-7356-1588-2' SELECT xCol FROM T WHERE xCol.exist ('/book[@ISBN = sql:variable("@isbn")]') = 1
可以用类似的方式使用 Sql:column(),并且提供附加的好处。可以使用列上的索引来提高效率,这要由基于成本的查询优化器决定。而且,计算列可以存储提升的属性,如基于 XML 数据类型的计算列中所述。
用于原生 XML 支持的目录视图
- 目录视图的目的是提供与 XML 用法有关的元数据信息。下面讨论了其中几个目录视图。
XML 索引
XML 索引项出现在目录视图 sys.indexes 中,索引"type"为 3。"name"列包含 XML 索引的名称。
XML 索引还被记录在目录视图 sys.xml_indexes 中,它包含 sys.indexes 的所有列以及一些对 XML 索引有意义的特殊列。列"secondary_type"中的值 NULL 表示主 XML 索引;值"P"、"R"和"V' "分别代表 PATH、PROPERTY 和 VALUE 辅助 XML 索引。
XML 索引的空间利用率可以在表值函数 sys.fn_indexinfo() 中找到。该函数会提供许多信息,例如,所占用的磁盘页数、平均行大小(字节)、记录数以及所有索引类型(包括 XML 索引)的其他信息。对于每个数据库分区都会提供这些信息;XML 索引使用基表的相同分区方案和分区函数。
示例:XML 索引的空间利用率
SELECT sum(Pages) FROM sys.fn_indexinfo ('T', 'idx_xCol_Path' , DEFAULT, 'DETAILED')
这会产生表 T 中的 XML 索引 idx_xCol_Path 在所有分区中占用的磁盘页数。如果不使用 sum() 函数,结果将返回每个分区的磁盘页利用率。
检索 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(schemaName, XmlSchemacollectionName, namespace-uri) 可产生一个 XML 数据类型实例,该实例包含 XML 架构集合中所含架构(预定义的 XML 架构除外)的 XML 架构片段。
可以用下列方式来枚举 XML 架构集合的内容:
在 XML 架构集合的适当目录视图上编写 T-SQL 查询。
使用内置函数 XML_SCHEMA_NAMESPACE()。可以在该函数的输出上应用 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 架构集合的内容
下面的语句枚举了关系架构 dbo 中的 XML 架构集合"myCollection"的内容。
SELECT XML_SCHEMA_NAMESPACE (N'dbo', N'myCollection')
通过将目标命名空间指定为 XML_SCHEMA_NAMESPACE() 的第三个参数,可以按 XML 数据类型实例的形式获取该集合中的单独 XML 架构,如下所示。
示例:输出 XML 架构集合中的指定架构
下面的语句从关系架构 dbo 中的 XML 架构集合"myCollection"中输出目标命名空间为"**https://www.microsoft.com/books**"的 XML 架构。
SELECT XML_SCHEMA_NAMESPACE (N'dbo', N'myCollection', N'https://www.microsoft.com/books')
查询 XML 架构
如果您需要查询已经加载到 XML 架构集合中的 XML 架构,可以采用下列方式:
在 XML 架构命名空间的目录视图上编写 T-SQL 查询。
除了将 XML 架构加载到 XML 类型系统中以外,创建一个包含 XML 数据类型列的表来存储 XML 架构。您可以使用 XML 数据类型方法来查询 XML 列。而且,您可以在该列上生成 XML 索引。然而,需要由应用程序来维护存储在 XML 列中的 XML 架构与存储在 XML 类型系统中的 XML 架构之间的一致性。例如,如果您从 XML 类型系统中删除了 XML 架构命名空间,则还必须从表中删除该命名空间以保持一致性。