MS SQL SERVER2005 XML 最佳实践

SQL Server 2005 为 XML 数据处理提供了广泛支持。可以将 XML 值以本机方式存储在 xml 数据类型列中,后者可以根据 XML 架构的集合进行类型化,也可以保持非类型化状态。可以对 XML 列创建索引。此外,通过使用 XQuery 和 XML DML 可支持精细的数据操作。XML DML 是针对数据修改的扩展。

SQL Server 2000 和 SQLXML Web 版本提供了强大的 XML 数据管理功能。这些功能着重于关系数据和 XML 数据之间的映射。可以使用带批注的 XSD (AXSD) 来定义关系数据的 XML 视图,以提供以 XML 为中心的方法,该方法支持对 XML 数据执行大容量数据加载、查询和更新功能。Transact-SQL 扩展提供了一个以 SQL 为中心的方法,以便使用 FOR XML 将关系查询结果映射到 XML,以及使用 OPENXML 从 XML 生成关系视图。这些支持已在 SQL Server 2005 中进行了扩展。结合新添加的本机 XML 支持,SQL Server 2005 提供了一个强大的平台,以针对半结构化和非结构化数据管理开发功能丰富的应用程序。

本主题提供了 SQL Server 2005 中的 XML 数据建模和使用准则,包含下列两个部分:

  • 数据建模

    可以通过使用本机 xml 数据类型和拆分到表中的 XML,以多种方式在 SQL Server 2005 中存储 XML 数据。本主题提供了为对 XML 数据进行建模做出适当选择的准则。另外,还介绍了对 XML 数据创建索引、属性提升和 XML 实例的类型化。

  • 使用

    本部分讨论了与使用相关的主题,例如将 XML 数据加载到服务器和查询编译中的类型推理。本部分还解释和区分了密切相关的功能,并就如何适当使用这些功能提出了建议。这些均通过示例进行了说明。

数据建模

本部分概述了应使用 SQL Server 2005 中的 XML 功能的理由,另外还提供了在本机 XML 存储和 XML 视图技术之间进行选择的准则,并给出了数据建模建议。

关系或 XML 数据模型

如果您的数据是高度结构化的,具有已知架构,则对于数据存储,关系模型可能最适用。SQL Server 提供了您可能需要的必要功能和工具。另一方面,如果结构是半结构化或非结构化的或者未知的,则必须考虑对这类数据进行建模。

如果您需要一个与平台无关的模型,以便通过使用结构和语义标记来确保数据的可移植性,则 XML 是一个不错的选择。此外,下列情况下,适于做此选择:

  • 您的数据为稀疏数据,或您不了解数据的结构,或数据结构将来可能会有重大变化。

  • 您的数据体现的是包容层次结构而不是在实体间的引用,并且可能是递归数据。

  • 您的数据本身具有顺序性。

  • 您希望基于数据的结构查询数据或更新部分数据。

如果上述条件均不满足,则应使用关系数据模型。例如,如果数据为 XML 格式,但应用程序只是使用数据库来存储和检索数据,则只需要 [n]varchar(max) 列。将数据存储在 XML 列中还有其他好处,包括让引擎确定数据格式是否正确或有效,以及支持对 XML 数据进行精细查询和更新。

在 SQL Server 2005 中存储 XML 数据的理由

下面是一些使用 SQL Server 2005 中的本机 XML 功能而不是在文件系统中管理 XML 数据的理由:

  • 您希望以一种高效的事务处理方式来共享、查询和修改 XML 数据。精细的数据访问对于您的应用程序而言很重要。例如,您可能需要提取 XML 文档中的某些部分,或者您可能需要插入新的部分而不是替换整个文档。

  • 您有关系数据和 XML 数据,希望在应用程序中进行关系数据和 XML 数据之间的互操作。

  • 您需要语言支持,以便对于跨域应用程序可以进行查询和数据修改。

  • 您希望服务器能够保证数据格式正确,并能够视情况根据 XML 架构来验证您的数据。

  • 您希望对 XML 数据创建索引以实现高效的查询处理和良好的可伸缩性,并使用一流查询优化器。

  • 您希望对 XML 数据进行 SOAP、ADO.NET 和 OLE DB 访问。

  • 您希望使用数据库服务器的管理功能来管理 XML 数据。例如,这可能是备份、恢复和复制。

如果上述条件均不满足,最好将数据存储为非 XML 大型对象类型,如 [n]varchar(max) 或 varbinary(max)。

XML 存储选项

SQL Server 2005 中的 XML 存储选项包括:

  • 采用 xml 数据类型的本机存储

    数据以保留数据的 XML 内容的内部表示形式进行存储,XML 内容包括包容层次结构、文档顺序、元素和属性值等。具体来说,就是保留 XML 数据的 InfoSet 内容。有关 InfoSet 的详细信息,请访问 text xmlns="http://ddue.schemas.microsoft.com/authoring/2003/5"> http://www.w3.org/TR/xml-infoset。InfoSet 内容并不是文本 XML 的精确副本,因为其中未保留下列信息:无关紧要的空格、属性顺序、命名空间前缀和 XML 声明。

    对于类型化的 xml 数据类型(即绑定到 XML 架构的 xml 数据类型),后架构验证 InfoSet (PSVI) 将类型信息添加到 InfoSet,并以内部表示形式进行编码。这会显著提高分析速度。有关详细信息,请参阅 text xmlns="http://ddue.schemas.microsoft.com/authoring/2003/5"> http://www.w3.org/TR/xmlschema-1 text xmlns="http://ddue.schemas.microsoft.com/authoring/2003/5"> http://www.w3.org/TR/xmlschema-2 上的 W3C XML 架构规范。

  • 在 XML 和关系存储之间映射

    通过使用带批注的架构 (AXSD),将 XML 分解到一个或多个表中的列。这可保留在关系级别上的数据保真度。因此,尽管忽略了元素间的顺序,但仍保留了层次结构。架构不能是递归的。

  • 大型对象存储 [n]varchar(max) 和 varbinary(max)

    存储数据的精确副本。这对于特殊用途的应用(如法律文档)很有用。大多数应用不需要完全相同的副本,且 XML 内容(InfoSet 保真度)即可满足需要。

通常,您必须结合使用这些方法。例如,您可能需要将 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 格式的产品手册,其中每个主题对应单独的一章,而每章中又包含多节。一节可以包含多个小节。因此,<section> 是一个递归元素。产品手册包含大量混合内容、关系图和技术材料;数据是半结构化的。用户可能希望对感兴趣的主题执行上下文搜索,例如,在有关“索引”的章中搜索有关“聚集索引”的节,并查询技术数量。

适于您的 XML 文档的存储模型是 xml 数据类型列。这可保留 XML 数据的 InfoSet 内容。对 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 数据,并将其分解到基础表。

相关示例包括显示为用于数据交换和 Web 服务的 XML 的关系数据。有关详细信息,请参阅 text xmlns="http://ddue.schemas.microsoft.com/authoring/2003/5"> MSDN Online Library

示例:使用带批注的 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 实例以便进行更新会导致多用户情况下的吞吐量下降。另一方面,过度分解会丢失对象封装,并增加重新汇集开销。

对于良好的设计而言,重要的是保持数据建模要求与锁定和更新特征之间的平衡。但在 SQL Server 2005 中,实际存储的 XML 实例的大小并不十分重要。

例如,通过使用对部分二进制大型对象 (BLOB) 和部分索引更新(将存储的现有 XML 实例与其更新后的版本进行比较)的新支持,对 XML 实例进行更新。部分二进制大型对象 (BLOB) 更新在两个 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 文档或内容。但是,必须使用标志指定在声明时是存储文档还是存储内容。此外,必须提供 XML 架构集合。如果每个 XML 实例都刚好有一个顶级元素,请指定 DOCUMENT。否则,请使用 CONTENT。查询编译器在编译查询过程中的类型检查中使用 DOCUMENT 标志以推断单一的顶级元素。

除了对 XML 列进行类型化之外,还可以对类型化或非类型化的 xml 数据类型列使用关系(列或行)约束。在下列情况下,请使用约束:

  • 无法在 XML 架构中表达业务规则。例如,花店的交货地址必须在其营业地点周围 50 英里之内。这可以编写为 XML 列的约束。约束可能涉及 xml 数据类型方法。

  • 您的约束涉及表中的其他 XML 列或非 XML 列。例如,强制使 XML 实例中的客户 ID (/Customer/@CustId) 与 CustomerID 关系列中的值匹配。

文档类型定义 (DTD)

可以使用 XML 架构来对 xml 数据类型列、变量和参数进行类型化,但不能使用 DTD 进行此项操作。但是,内联 DTD 既可用于非类型化的 XML,也可用于类型化的 XML,以便提供默认值,并将实体引用替换为其扩展形式。

可以通过使用第三方工具将 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 的详细信息,请参阅 xml 数据类型。为简化起见,针对 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 创建 XML 索引(名为 idx_xCol):

 复制代码
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 索引可能会提高工作负荷的处理速度。最常见的情况是在 Transact-SQL 的 WHERE 子句中对 XML 列使用 exist() 方法。

  • 如果工作负荷通过使用路径表达式从单个 XML 实例中检索多个值,则在 PROPERTY 索引中聚集各个 XML 实例中的路径可能会很有用。这种情况通常出现在属性包方案中,此时提取对象的属性并且已知其主键值。

  • 如果工作负荷涉及查询 XML 实例中的值,但不知道包含那些值的元素名称或属性名称,则您可能希望创建 VALUE 索引。这通常出现在 descendant 轴查找中,例如 //author[last-name="Howard"],其中 <author> 元素可以出现在层次结构的任何级别上。这种情况也出现在通配符查询中,例如 /book [@* = "novel"],其中查询将查找具有某个值为“novel”的属性的 <book> 元素。

示例:基于路径的查找

为了进行说明,假定以下查询在您的工作负荷中很常见:

 复制代码
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/text())[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
示例:基于值的查询

在下面的查询中,descendant-or-self (//) 指定部分路径,以便基于 ISBN 值的查找从 VALUE 索引的使用中获益。

 复制代码
SELECT xCol
FROM    T
WHERE    xCol.exist ('//book/@ISBN[. = "0-7356-1588-2"]') = 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 值进行全文搜索

通常不能消除上一个示例中执行的 XQuery contains() 检查。请看下面的查询:

 复制代码
SELECT * 
FROM   T 
WHERE  CONTAINS(xCol,'run') 

因为使用了词干匹配,所以文档中的“ran”一词匹配搜索条件。此外,不通过使用 XQuery 来检查搜索上下文。

当通过使用 AXSD 将 XML 分解为全文索引的关系列时,对 XML 视图执行的 XPath 查询不对基础表执行全文搜索。

属性提升

如果主要是对少数元素和属性值进行查询,您可能希望将那些数量提升到关系列。检索整个 XML 实例,但只对一小部分 XML 数据进行查询时,这很有用。不必对 XML 列创建 XML 索引。但可以对提升的列创建索引。必须编写查询来使用提升的列。也就是说,查询优化器不会将对 XML 列的查询再定向到提升的列。

提升的列可以是同一个表中的计算列,也可以是表中用户维护的单独列。从每个 XML 实例提升单一值时,这就足够了。但是,对于多值属性,则必须为属性创建单独的表,如以下部分所述。

基于 xml 数据类型的计算列

可以使用调用 xml 数据类型方法的用户定义函数来创建计算列。计算列的类型可以是任何 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 的 <book>:

 复制代码
SELECT xCol
FROM   T
WHERE  xCol.exist('/book/@ISBN[. = "0-7356-1588-2"]') = 1

可以重新编写对 XML 列的查询以使用计算列,如下所示:

 复制代码
SELECT xCol
FROM   T
WHERE  ISBN = '0-7356-1588-2'

您可以创建返回 xml 数据类型的用户定义函数,并使用用户定义函数来创建计算列。但是,不能对 XML 计算列创建 XML 索引。

创建属性表

您可能希望将 XML 数据中的某些多值属性提升到一个或多个表中,对那些表创建索引,并再次定向查询以使用这些表。典型的情况是少数属性占了大部分查询工作负荷。您可以执行下列操作:

  • 创建一个或多个表来保存多值属性。您会发现可以很方便做到:每个表存储一个属性,以及在属性表中复制基表的主键以便与基表进行后向联接。

  • 如果希望维护属性的相对顺序,必须为相对顺序引入一个单独的列。

  • 为 XML 列创建触发器以维护属性表。在触发器中,执行下列操作之一:

    • 使用 xml 数据类型方法(如 nodes() 和 value())来插入和删除属性表的行。

    • 在公共语言运行时 (CLR) 中创建流式表值函数来插入和删除属性表的行。

    • 编写对属性表进行 SQL 访问的查询和对基表中的 XML 列进行 XML 访问的查询,这些表之间通过主键联接起来。

示例:创建属性表

为了进行说明,假定您希望提升作者的名字。书有一个或多个作者,因此名字为多值属性。每个名字都存储在属性表的单独行中。在属性表中复制基表的主键以便进行后向联接。

 复制代码
create table tblPropAuthor (propPK int, propAuthor varchar(max))
示例:创建用户定义函数以从 XML 实例生成行集

以下表值函数 udf_XML2Table 接受主键值和 XML 实例。它检索 <book> 元素的所有作者的名字,然后返回主键-名字对行集。

 复制代码
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 流式表值函数的解决方案

此解决方案包括下列步骤:

  1. 定义 CLR 类 SqlReaderBase,它实现 ISqlReader,并通过在 XML 实例上应用路径表达式来生成流式表值输出。

  2. 创建程序集和 Transact-SQL 用户定义函数来启动该 CLR 类。

  3. 通过使用用户定义函数来定义插入、更新和删除触发器,以维护属性表。

若要如此,首先创建流式 CLR 函数。xml 数据类型显示为 ADO.NET 中的托管类 SqlXml,支持返回 XmlReader 的 CreateReader() 方法。

注意:
本部分中的示例代码使用了 XPathDocument 和 XPathNavigator。这些都强制要求您将所有 XML 文档加载到内存中。如果您要在您的应用程序中使用类似代码来处理多个大型 XML 文档,此代码并不可伸缩。而是应尽可能保持较小的内存分配并使用流式接口。有关性能的详细信息,请参阅 Architecture of CLR Integration 。

 

 

 复制代码
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 对应的 Transact-SQL 用户定义函数 SQL_streaming_xml_tvf(不显示)。该用户定义函数用于定义表值函数 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 版本相同。但是,更新触发器只是用 CLR_udf_XML2Table() 函数替换了函数 udf_XML2Table()。

XML 架构集合

XML 架构集合是关系架构作用域内的元数据实体。它包含一个或多个可能相关(如通过 <xs:import>)也可能无关的 XML 架构。XML 架构集合中各个 XML 架构通过使用其目标命名空间来标识。

XML 架构集合是通过使用 CREATE XML SCHEMA COLLECTION (Transact-SQL) 语法并提供一个或多个 XML 架构来创建。可以通过使用 ALTER XML SCHEMA COLLECTION (Transact-SQL) 语法,将多个 XML 架构组件添加到现有 XML 架构中,并将多个架构添加到 XML 架构集合中。可以通过使用 SQL Server 2005 中的安全模式像任何 SQL 对象那样保证 XML 架构集合的安全。

多类型化列

XML 架构集合 C 根据多个 XML 架构对 XML 列 xCol 进行类型化。此外,DOCUMENT 标志和 CONTENT 标志指定是否可以将 XML 树或片段分别存储在 xCol 列中。

对于 DOCUMENT,每个 XML 实例都指定实例中顶级元素的目标命名空间,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 或 image 中,则可以使用 SQL Server Integration Services 将表导入 SQL Server 2005 数据库中。使用 ALTER TABLE 语句将列类型更改为 XML。

  • 可以使用 bcp out 从 SQL Server 2000 大容量复制数据,然后使用 bcp in 将数据大容量插入 SQL Server 2005 数据库中。

  • 如果将数据存储在 SQL Server 2000 数据库的关系列中,请创建具有 [n]text 列和(可选)用于行标识符的主键列的新表。使用客户端编程检索在服务器上通过 FOR XML 生成的 XML,并将其写入 [n]text 列。然后,使用上述方法将数据传输到 SQL Server 2005 数据库中。您可以选择将 XML 直接写入 SQL Server 2005 数据库中的 XML 列。

示例:将列类型更改为 XML

假定您希望将 R 表中的 [n]text 列或 image 列 XYZ 的类型更改为非类型化的 XML。以下语句执行此类型更改:

 复制代码
ALTER TABLE R ALTER COLUMN XYZ XML
  • 如果需要,可以通过指定 XML 架构集合将目标为类型化为 XML。

大容量加载 XML 数据

可以通过使用 SQL Server 的大容量加载功能(如 bcp)将 XML 数据大容量加载到服务器中。通过使用 OPENROWSET 可以将文件中的数据加载到 XML 列中。以下示例说明了这一点。

示例:从文件中加载 XML

此示例显示了如何在表 T 中插入行。从文件 C:/MyFile/xmlfile.xml 中将 XML 列的值作为 CLOB 加载,并为整数列提供了值 10。

 复制代码
INSERT INTO T
SELECT 10, xCol
FROM    (SELECT *    
    FROM OPENROWSET (BULK 'C:/MyFile/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 和类型推理

Transact-SQL 中嵌入的 XQuery 语言支持查询 xml 数据类型。该语言正在由万维网联盟 (W3C) 开发,Microsoft 的所有主要数据库供应商都参与其中。它包含了 XPath 2.0 版作为导航语言。同时,还提供了针对 xml 数据类型的数据修改语言构造。有关 SQL Server 中支持的 XQuery 构造、函数和运算符的详细信息,请参阅针对 xml 数据类型的 XQuery 函数

错误模型

语法不正确的 Xquery 表达式和 XML DML 语句会返回编译错误。编译阶段会检查 XQuery 表达式和 DML 语句的静态类型正确性,并针对类型化的 XML 使用 XML 架构进行类型推理。如果表达式在运行时由于类型安全冲突而失败,会引起静态类型错误。静态错误的示例包括将字符串添加到整数,以及在不存在的节点中查询类型化的数据。

与 W3C 标准有所不同的是,XQuery 运行时错误被转换为空序列。这些序列根据调用上下文,可以作为空 XML 或 NULL 传播到查询结果。

通过显式转换为正确的类型,用户可以解决静态错误的问题,尽管运行时转换错误将被转换为空序列。

下列部分详细介绍了类型检查。

单一性检查

如果编译器无法确定是否在运行时保证单一性,则要求单一性的位置步骤、函数参数和运算符将返回错误。此问题经常出现在非类型化数据上。例如,对属性的查找需要单一的父元素。选择单个父节点的序号即可满足需要。计算 node()-value() 组合以提取属性值可能不需要指定序号。如以下示例中所示。

示例:已知单一性

在此示例中,nodes() 方法为每个 <book> 元素生成一个单独的行。对 <book> 节点进行计算的 value() 方法提取 @genre 值,其作为属性,具有单一性。

 复制代码
SELECT nref.value('@genre', 'varchar(max)') LastName
FROM   T CROSS APPLY xCol.nodes('//book') AS R(nref)

XML 架构用于对类型化的 XML 进行类型检查。如果某个节点指定为 XML 架构中单一的节点,则编译器将使用该信息,并且不会发生任何错误。否则,需要选择单个节点的序号。特别的情况是,使用 descendant-or-self (//)(如在 /book//title 中)会丢失 <title> 元素的单一性基数推理,即使 XML 架构指定其如此。因此,您应该将其重写为 (/book//title)[1]。

对于类型检查,务必注意 //first-name[1] 和 (//first-name)[1] 之间的差异。前者返回一组 <first-name> 节点,其中每个节点都是其同级节点间最左侧的 <first-name> 节点。后者返回 XML 实例中按文档顺序的第一个单一的 <first-name> 节点。

示例:使用 value()

下面对非类型化的 XML 列的查询导致发生静态的编译错误。这是因为 value() 希望将一个单一节点作为第一个参数,而编译器无法确定在运行时是否将仅有一个 <last-name> 节点:

 复制代码
SELECT xCol.value('//author/last-name', 'nvarchar(50)') LastName
FROM   T

可以考虑下面的解决办法:

 复制代码
SELECT xCol.value('//author/last-name[1]', 'nvarchar(50)') LastName
FROM   T

但是,该解决办法不解决错误,因为在每个 XML 实例中可能会有多个 <author> 节点。采用下面的重写代码可以解决问题:

 复制代码
SELECT xCol.value('(//author/last-name/text())[1]', 'nvarchar(50)') LastName
FROM   T

此查询返回每个 XML 实例中第一个 <last-name> 元素的值。

parent 轴

如果无法确定节点的类型,它将成为 anyType。这不会隐式转换为任何其他类型。在使用 parent 轴(如 xCol.query('/book/@genre/../price'))进行导航的过程中,尤其会发生这种情况。父节点类型确定为 anyType。在 XML 架构中,也可以将元素定义为 anyType。在这两种情况下,丢失更为精确的类型信息经常会导致发生静态类型错误,并需要将原子值显式转换为其特定类型。

Data()、text() 和 string() 取值函数

XQuery 有一个从节点提取类型化标量值的函数 fn:data()、一个返回文本节点的节点测试 text(),以及一个返回节点的字符串值的函数 fn:string()。它们的用法容易混淆。以下是在 SQL Server 2005 中正确使用它们的准则。使用 XML 实例 <age>12</age> 进行说明。

  • 非类型化的 XML:路径表达式 /age/text() 返回文本节点“12”。函数 fn:data(/age) 返回字符串值“12”,fn:string(/age) 也是如此。

  • 类型化的 XML:对于任何简单的类型化的 <age> 元素,表达式 /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() 的参数中的 <r> 元素的不同类型(xs:int、xs:float 或 xs:double)的值求和。为了解决此问题,请将函数调用重写为 fn:avg(for $r in //r return $r cast as xs:double ?)。

示例:联合类型的运算符

加法运算(“+”)要求使用精确类型的操作数。因此,表达式 (//r)[1] + 1 返回静态错误,该错误包含前面所述的 <r> 元素的类型定义。一个解决方法是将其重写为 (//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/text())[1]', 'nvarchar(50)') FirstName,
       nref.value('(last-name/text())[1]', 'nvarchar(50)') LastName
FROM   T CROSS APPLY xCol.nodes('//author') AS R(nref)
WHERE  nref.exist('first-name[. != "David"]') = 1

在此示例中,nodes('//author') 生成一个由对每个 XML 实例的 <author> 元素引用组成的行集。通过计算与那些引用相关的 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 数据的边缘表格式。另外,也可以通过它在单独的“overflow”列中使用其余的 XML 值。

将 nodes() 和 value() 函数结合起来可有效地使用 XML 索引。因此,与 OpenXml 相比,这种结合有更高的可伸缩性。

使用 FOR XML 从行集生成 XML

可以通过在 FOR XML 中使用新的 TYPE 指令,从行集生成 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 类型的 columnxmlVal。可以查询像常规 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 反序列化为一个或多个托管类,并使用托管代码对它们进行操作。

  • 您可以编写 Transact-SQL 存储过程和函数,对 XML 列进行处理以满足业务需要。

示例:应用 XSL 转换

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

 复制代码
public static SqlXml TransformXml (SqlXml XmlData, string xslPath) {
   // Load XSL transformation
   XslCompiledTransform xform = new XslCompiledTransform();
   XPathDocument xslDoc = new XPathDocument (xslPath);
   xform.Load(xslDoc);

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

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

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

 复制代码
SELECT SqlXslTransform (xCol, 'C:/MyFile/xsltransform.xsl')
FROM    T
WHERE  xCol.exist('/book/title/text()[contains(.,"custom")]') =1

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

SQLCLR 扩展了这样一些功能:将 XML 数据分解到多个表或属性提升,以及通过使用 System.Xml 命名空间中的托管类查询 XML 数据。有关详细信息,请参阅 SQL Server 联机丛书和 .Net Framework SDK 文档。

跨域查询

当您的数据同时保存在关系和 xml 数据类型列中时,您可能希望编写将关系和 XML 数据处理结合起来的查询。例如,您可以通过使用 FOR XML 将关系列和 XML 列中的数据转换为 xml 数据类型实例,并使用 XQuery 对其进行查询。相反,您可以从 XML 值生成行集,并使用 Transact-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 索引项位于目录视图 sys.indexes 中,索引“type”为 3。名称列包含 XML 索引的名称。

另外,XML 索引还记录在目录视图 sys.xml_indexes 中。此视图包含 sys.indexes 的所有列和对 XML 索引有用的某些特定列。secondary_type 列中的值 NULL 表示主 XML 索引;值“P”、“R”和“V”分别表示 PATH、PROPERTY 和 VALUE 辅助 XML 索引。

可以在表值函数 sys.dm_db_index_physical_stats 中找到 XML 索引的空间使用情况。它提供了所有索引类型的相关信息,例如,占用的磁盘页数、平均行大小(字节)和记录数。其中也包括 XML 索引。对于每个数据库分区,都提供此信息。XML 索引使用基表的相同分区方案和分区函数。

检索 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 架构集合的相应目录视图的 Transact-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”输出目标命名空间为“http://www.microsoft.com/books”的 XML 架构。

 复制代码
SELECT XML_SCHEMA_NAMESPACE (N'dbo', N'myCollection', 
N'http://www.microsoft.com/books')

查询 XML 架构

可以按下列方式查询加载到 XML 架构集合的 XML 架构:

  • 编写对 XML 架构命名空间的目录视图的 Transact-SQL 查询。

  • 创建包含 xml 数据类型列的表以存储 XML 架构,并将这些架构加载到 XML 类型系统中。可以通过使用 xml 数据类型方法查询 XML 列。另外,还可以对此列生成 XML 索引。但是,使用此方法时,应用程序必须保持 XML 列中存储的 XML 架构和 XML 类型系统之间的一致性。例如,如果从 XML 类型系统中删除 XML 架构命名空间,还必须从表中删除它以保持一致性。
     
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值