Sql 服务器:选择性 XML 索引未被有效使用

Sql Server: Selective XML Index not being efficiently used

我正在探索提高应用程序性能的方法,我只能在有限的程度上影响数据库级别。 SQL 服务器版本是 2012 SP2,table 和有问题的视图结构是(我不能真正影响这个 + 请注意 xml 文档可能总共有几百个元素):

CREATE TABLE Orders(
    id nvarchar(64) NOT NULL,
    xmldoc xml NULL,
    CONSTRAINT PK_Order_id PRIMARY KEY CLUSTERED (id)
);

CREATE VIEW V_Orders as
SELECT 
    a.id, a.xmldoc
    ,a.xmldoc.value('data(/row/c1)[1]', 'nvarchar(max)') "Stuff"
    ,a.xmldoc.value('data(/row/c2)[1]', 'nvarchar(max)') "OrderType"
etc..... many columns
from Orders a;

一个典型的查询(以及下面用于测试的查询):

SELECT id FROM V_Orders WHERE OrderType = '30791'

所有查询都是针对视图执行的,我既不能影响查询也不能影响 table/view 结构。

我认为向 table 添加选择性 XML 索引将是我的救星:

CREATE SELECTIVE XML INDEX I_Orders_OrderType ON Orders(xmldoc)
FOR(
    pathOrderType = '/row/c2' as SQL [nvarchar](20)
)

但即使在更新统计信息之后,执行计划看起来也很奇怪。无法 post 将图片作为新帐户,因此相关详细信息作为文本:

实际上,对于我的测试数据,查询甚至没有 return 任何结果,但是 return 是一个还是几个没有任何区别。执行时间支持查询真正花费的时间,只要可以从执行计划中推断出来并且读取计数为数千。

所以我的问题是为什么优化器没有正确使用选择性 xml 索引?还是我弄错了什么?我将如何使用选择性 xml 索引(或者可能是持久化列)优化此特定查询的性能?

编辑: 我对更大的样本数据进行了额外的测试(table 中的约 274k 行,XML 文档接近平均生产规模)并将选择性 XML 索引与提升的列进行比较。结果来自 Profiler 跟踪,集中于 CPU 使用和读取计数。选择性 xml 索引的执行计划与上述内容基本相同。

选择性 XML 索引和 274k 行(执行上面的查询): CPU:6454,读取:938521

将搜索字段中的值更新为唯一值后(总记录数仍为 274k),我得到以下结果:

选择性 XML 索引和 274k 行(执行上面的查询): CPU: 10077, 读取: 1006466

然后使用提升的(即持久化的)单独索引列并直接在视图中使用它: CPU: 0, 读取: 23

选择性 XML 索引性能似乎比 SQL 索引列提取更接近完整 table 扫描。我在某处读到,对 table 使用模式可能有助于从执行计划中删除 TOP N 步骤(假设我们正在搜索非重复字段),但我不确定在这种情况下这是否是现实的可能性.

您创建的选择性 XML 索引存储在内部 table 中,其中 Orders 中的主键作为内部 [=55= 的聚集键的前导列] 和指定的路径存储为稀疏列。

您获得的查询计划可能如下所示:

您对整个订单 table 进行了扫描,并在内部 table 中搜索了订单中每一行的主键。最后一个 Filter 运算符负责检查 OrderType 的值,只返回匹配的行。

实际上并不是您对索引的期望。

辅助选择性 XML 索引来拯救。它们是为主要选择性索引中指定的路径之一创建的,并将在路径表达式中提取的值上创建一个 non-clustered 键。

然而,这并不是那么容易。 SQL 服务器将不会对 values() 函数提取的值所使用的谓词使用二级索引。您必须改用 exists()。此外,exists() 要求在路径表达式中使用 XQUERY 数据类型,其中 value() 使用 SQL 数据类型。

您的主要选择性 XML 索引可能如下所示:

CREATE SELECTIVE XML INDEX I_Orders_OrderType ON Orders(xmldoc)
FOR 
(
  pathOrderType = '/row/c2' as sql nvarchar(20), 
  pathOrderTypeX = '/row/c2/text()' as xquery 'xs:string' maxlength (20)
)

pathOrderTypeX.

上有第二个
CREATE XML INDEX I_Orders_OrderType2 ON Orders(xmldoc)
  USING XML INDEX I_Orders_OrderType FOR (pathOrderTypeX) 

通过使用 exist() 的查询,您将获得此计划。

select id
from V_Orders
where xmldoc.exist('/row/c2/text()[. = "30791"]') = 1

第一次查找是查找您在内部 table 的 non-clustered 索引中查找的值。密钥查找是在内部 table 上的聚集密钥上完成的(不知道为什么这是必要的)。最后一个搜索是在订单 table 中的主键上,然后是一个过滤器,用于检查列 xmldoc.

中的空值

如果您可以使用 property promotion,在 XML 的订单 table 中创建计算索引列,我想您仍然可以获得比使用辅助选择性更好的性能XML 索引。