如果编译器不能确定在运行时是否保证有 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 列中的选项 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 创建了一个新的元数据实体。
新闻热点
疑难解答