SQL2005用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 数据实例描述查询:
下面的语句在表 T 的 XML 列 xCol 上创建了名为 idx_xCol 的 XML 索引:
辅助 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" 的属性的 元素。 示例:基于路径的查找 假设下面的查询在您的工作负荷中很常见:
路径表达式 /book/@genre 和值 "novel" 对应于 PATH 索引的键字段。因此,PATH 类型的辅助 XML 索引对于该工作负荷很有用:
示例:获取对象的属性 请考虑下面的查询,它从表 T 的各个行中检索一本书的属性"genre"、"title"和 ISBN:
在这种情况下,属性索引很有用,其创建方式如下所示:
示例:基于值的查询 在以下查询中,子代轴或自身轴 (//) 指定了部分路径,以便基于 ISBN 值的查找可以因为使用 VALUE 索引而受益:
VALUE 索引按如下方式创建:
XML 列上的全文索引 您可以在 XML 列上创建全文索引,从而将 XML 值的内容编入索引,而忽略 XML 标记。属性值没有被编入全文索引(因为它们被视为标记的一部分),并且元素标记被用作标记边界。在某些情况下,可以将全文搜索与 XML 索引用法结合起来: • 首先,使用 SQL 全文搜索筛选感兴趣的 XML 值。 • 接下来,查询这些 XML 值,这会使用 XML 列上的 XML 索引。 示例:将全文搜索与 XML 查询结合起来 在 XML 列上创建全文索引之后,以下查询将检查 XML 值是否在书名中包含单词"custom":
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() 检查。请考虑查询:
因为使用单词衍生,所以文档中的单词"ran"匹配搜索条件。而且,使用 XQuery 时不会检查搜索上下文。 在使用被全文索引的 AXSD 将 XML 分解到关系列中时,XML 视图上的 XPath 查询不会对基础表执行全文搜索。 属性提升 如果主要是对少量元素和属性值进行查询(例如,基于客户 ID 查找客户,即指定了 /Customer/@CustId 的值),您可能希望将这些数量提升到关系列中。当检索了整个 XML 实例,但只对一小部分 XML 数据进行查询时,这将很有用。在 XML 列上创建 XML 索引是没有必要的;相反,可以将被提升的列编入索引。必须编写查询以使用提升的列(即,查询优化器不会将对 XML 列的查询重新定向到提升的列)。 提升的列可以是同一表中的计算列,也可以是表中单独的、用户维护的列。当从各个 XML 实例中提升唯一值(即单值属性)时,这已足够。然而,对于多值属性,您必须为该属性创建单独的表,如下所述。 基于 XML 数据类型的计算列 可以使用能够激活 XML 数据类型方法的用户定义函数 (UDF) 来创建计算列。计算列的类型可以是任何 SQL 类型,包括 XML。以下示例说明了这一点。 示例:基于 XML 数据类型方法的计算列 为书籍的 ISBN 创建用户定义的函数:
为 ISBN 向表中添加一个计算列:
可以用通常的方式将计算列编入索引。 示例:基于 XML 数据类型方法的计算列上的查询 要获取其 ISBN 为 0-7356-1588-2 的 ,可以改写 XML 列上的查询
以使用计算列,如下所示:
可以创建一个用户定义的函数,返回 XML 数据类型和使用该 UDF 的计算列。然而,无法在计算的 XML 列上创建 XML 索引。
创建属性表 您可能希望将 XML 数据中的某些多值属性提升到一个或多个表中,在这些表上创建索引,并且重定向查询以使用这些表。典型的情形是一小部分属性覆盖了大部分查询工作负荷。您可以执行以下操作: • 创建一个或多个表以存放多值属性。您可能发现采用以下处理方式会很方便:每个表存储一个属性,并且在属性表中复制基表的主键以便与基表进行向后联接。 • 如果您希望保持属性的相对顺序,则需要为相对顺序引入一个单独的列。 • 在 XML 列上创建触发器以便维护属性表。在触发器中,执行以下操作: • 使用 XML 数据类型方法(如 nodes() 和 value())在属性表中插入和删除行。(有关 nodes() 方法的详细信息,请参阅 Value()、Nodes() 和 OpenXML()。) • 在 CLR 中创建流式表值函数,以便在属性表中插入和删除行。 • 编写查询,以便对属性表进行 SQL 访问,以及对基表中的 XML 列进行 XML 访问,这需要使用这些表的主键将其相互联接。 示例:创建属性表 假设您希望提升作者的名字。书籍有一个或多个作者,因此名字是一个多值属性。每个名字都存储在属性表的单独行中。在属性表中复制了基表的主键以便向后联接。
示例:创建用户定义的函数以便从 XML 实例生成行集 下面的表值函数 udf_XML2Table 接受一个主键值和一个 XML 实例。它将检索 元素的所有作者的名字,并返回(主键,名字)对行集。
示例:创建触发器以填充属性表 插入触发器:在属性表中插入行
删除触发器:基于删除行的主键值,从属性表中删除行
更新触发器:在与更新的 XML 实例对应的属性表中删除现有行,并且在该属性表中插入新行
示例:查找作者的名字为"David"的 XML 实例 可以在 XML 列上表示该查询。另外,还可以在属性表中搜索名字"David",然后与基表执行向后联接以返回 XML 实例,如下所示:
示例:使用 CLR 流式表值函数的解决方案 该解决方案包括以下步骤:(a) 定义 CLR 类 SqlReaderBase,它实现了 ISqlReader,并且通过在 XML 实例上应用路径表达式来生成流式表值输出;(b) 创建一个程序集和一个 T-SQL 用户定义函数 (UDF) 来激活该 CLR 类;(c) 使用 UDF 定义插入、更新和删除触发器,以维护属性表。 首先,创建流式 CLR 函数,其主干如下所示。XML 数据类型被公开为 ADO.NET 中的托管类 SqlXml;它支持返回 XmlReader 的方法 CreateReader()。
接下来,创建一个程序集,以及一个与 CLR 函数 streaming_xml_tvf 对应的 T-SQL 用户定义函数 SQL_streaming_xml_tvf(未显示)。该 UDF 用于定义表值函数 CLR_udf_XML2Table 以便生成行集:
最后,定义触发器,如示例创建触发器以填充属性表中所示,但用函数 CLR_udf_XML2Table 替换 udf_XML2Table。因此,插入触发器将如下所示:
删除触发器与非 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 实例执行查询和数据修改。 (T114) |