设定:
create table dbo.T
(
ID int identity primary key,
XMLDoc xml not null
);
insert into dbo.T(XMLDoc)
select (
select N.Number
for xml path(''), type
)
from (
select top(10000) row_number() over(order by (select null)) as Number
from sys.columns as c1, sys.columns as c2
) as N;
每行的样本XML:
<Number>314</Number>
查询的工作是计算T
指定值为的行数<Number>
。
有两种显而易见的方法:
select count(*)
from dbo.T as T
where T.XMLDoc.value('/Number[1]', 'int') = 314;
select count(*)
from dbo.T as T
where T.XMLDoc.exist('/Number[. eq 314]') = 1;
事实证明,value()
并且exists()
需要选择性XML索引工作两种不同的路径定义。
create selective xml index SIX_T on dbo.T(XMLDoc) for
(
pathSQL = '/Number' as sql int singleton,
pathXQUERY = '/Number' as xquery 'xs:double' singleton
);
该sql
版本是value()
与xquery
版本是exist()
。
您可能会认为,像这样的索引将使您的计划具有很好的搜索效果,但是选择性的XML索引被实现为系统表,且主键T
为系统表的集群键的前导键。指定的路径是该表中的稀疏列。如果要为已定义路径的实际值创建索引,则需要创建一个辅助选择索引,每个路径表达式一个。
create xml index SIX_T_pathSQL on dbo.T(XMLDoc)
using xml index SIX_T for (pathSQL);
create xml index SIX_T_pathXQUERY on dbo.T(XMLDoc)
using xml index SIX_T for (pathXQUERY);
用于查询的查询计划exist()
在辅助XML索引中进行查找,然后在系统表中针对选择性XML索引进行键查找(不知道为什么需要这样做),最后进行查找T
以确保实际上存在那里的行。最后一部分是必要的,因为系统表和之间没有外键约束T
。
value()
查询的计划不是很好。它对T
嵌套表进行聚簇索引扫描,并与内部表上的搜索连接,以从稀疏列中获取值,最后对值进行过滤。
在优化之前确定是否应该使用选择索引,但是是否应该使用辅助选择索引是优化器基于成本的决定。
为什么在where子句过滤时不使用二级选择索引value()
?
更新:
查询在语义上是不同的。如果您添加带有值的行
<Number>313</Number>
<Number>314</Number>`
该exist()
版本将数2行和values()
查询也要算1排。但是,使用此处指定的索引定义(使用singleton
伪指令),SQL Server将阻止您添加包含多个<Number>
元素的行。
但是,这不能让我们在values()
未指定[1]
保证编译器仅获得单个值的情况下使用该函数。这[1]
就是我们在value()
计划中进行前N个排序的原因。
看来我在这里接一个答案...