sql-server – 可以优化涉及XQuery函数的SELECT查询,从具有任意数量的子元素的元素中进行选择

假设我有以下XML数据,其中

>< root>可以有任意数量的< child>孩子
>< child>可以有任意数量的< grandchild>孩子
><孙子>可以有任意数量的< greatgrandchild>孩子
>< child>和<孙子>可以有任意数量的<参数>孩子
>每个<参数>有一个< name>和一个< value>儿童
>伟大的孩子们的深度停止(不需要任意深度)

例如,

<root name="root">
  <child name="a">
    <grandchild name="a1">
      <parameter>
        <name>param1</name>
        <value>ABC123</value>
      </parameter>
      <parameter>
        <name>param2</name>
        <value>CBC</value>
      </parameter>
      <greatgrandchild name="a1a">
        <parameter>
          <name>paramA</name>
          <value>ABC</value>
        </parameter>
        <parameter>
          <name>paramB</name>
          <value>DBC</value>
        </parameter>
      </greatgrandchild>
    </grandchild>
  </child>
</root>

我需要做的是选择一个包含“_BC”的值的参数,其中_是一个不是A的字符.

我有以下查询

SELECT
    TestId,
    [root],
    child,
    grandchild,
    NULL AS greatgrandchild,
    parameter
FROM (
        SELECT
            TestId,
            [root].a.value('@name', 'varchar(max)') AS [root],
            child.b.value('@name', 'varchar(max)') AS child,
            grandchild.c.value('@name', 'varchar(max)') AS grandchild,
            parameter.d.value('(name)[1]', 'varchar(max)') AS parameter,
            parameter.d.value('(value)[1]', 'varchar(max)') AS parameter_value
        FROM
            dbo.TestTable
            CROSS APPLY TestData.nodes('/root') AS [root](a)
            CROSS APPLY [root].a.nodes('child') AS child(b)
            CROSS APPLY child.b.nodes('grandchild') AS grandchild(c)
            CROSS APPLY grandchild.c.nodes('parameter') AS parameter(d)
    ) t
WHERE
    parameter_value LIKE '%[^Aa]BC%'
UNION ALL
SELECT
    TestId,
    [root],
    child,
    grandchild,
    greatgrandchild,
    parameter
FROM (
        SELECT
            TestId,
            [root].a.value('@name', 'varchar(max)') AS [root],
            child.b.value('@name', 'varchar(max)') AS child,
            grandchild.c.value('@name', 'varchar(max)') AS grandchild,
            greatgrandchild.d.value('@name', 'varchar(max)') AS greatgrandchild,
            parameter.e.value('(name)[1]', 'varchar(max)') AS parameter,
            parameter.e.value('(value)[1]', 'varchar(max)') AS parameter_value
        FROM
            dbo.TestTable
            CROSS APPLY TestData.nodes('/root') AS [root](a)
            CROSS APPLY [root].a.nodes('child') AS child(b)
            CROSS APPLY child.b.nodes('grandchild') AS grandchild(c)
            CROSS APPLY grandchild.c.nodes('greatgrandchild') AS greatgrandchild(d)
            CROSS APPLY greatgrandchild.d.nodes('parameter') AS parameter(e)
    ) t
WHERE
    parameter_value LIKE '%[^Aa]BC%'

例如,对于以下测试数据:

CREATE TABLE TestTable (
    TestId INT PRIMARY KEY,
    TestData XML NOT NULL
);

INSERT INTO dbo.TestTable (TestId, TestData)
VALUES (1, '<root name="root">
  <child name="a">
    <grandchild name="a1">
      <parameter>
        <name>param1</name>
        <value>ABC123</value>
      </parameter>
      <parameter>
        <name>param2</name>
        <value>CBC</value>
      </parameter>
      <greatgrandchild name="a1a">
        <parameter>
          <name>paramA</name>
          <value>ABC</value>
        </parameter>
        <parameter>
          <name>paramB</name>
          <value>DBC</value>
        </parameter>
      </greatgrandchild>
    </grandchild>
  </child>
</root>');

应返回以下结果集:

TestId root child grandchild greatgrandchild parameter
------------------------------------------------------
1      root a     a1         NULL            param2
1      root a     a1         a1a             paramB

我想知道我的SELECT查询是否可以优化

在每行包含20KB XML数据的400行表中,SELECT查询需要40分钟.无法更改XML布局.

最佳答案
这是对已有内容的重写.

>根节点上不需要粉碎.
>为参数名称和参数值指定text()节点.

在我的有限测试中,它显着加快了速度.我期待着看到你身边有什么表现增益.

select T.TestId,
       T.TestData.value('(/root/@name)[1]', 'varchar(max)') as [root],
       C.X.value('@name', 'varchar(max)') as child,
       GC.X.value('@name', 'varchar(max)') as grandchild,
       null as greatgrandchild,
       P.X.value('(name/text())[1]', 'varchar(max)') as parameter
from dbo.TestTable as T
  cross apply T.TestData.nodes('/root/child') as C(X)
  cross apply C.X.nodes('grandchild') as GC(X)
  cross apply GC.X.nodes('parameter') as P(X)
where P.X.value('(value/text())[1]', 'varchar(max)') like '%[^Aa]BC%'
union all
select T.TestId,
       T.TestData.value('(/root/@name)[1]', 'varchar(max)') as [root],
       C.X.value('@name', 'varchar(max)') as child,
       GC.X.value('@name', 'varchar(max)') as grandchild,
       GGC.X.value('@name', 'varchar(max)') as greatgrandchild,
       P.X.value('(name/text())[1]', 'varchar(max)') as parameter
from dbo.TestTable as T
  cross apply T.TestData.nodes('/root/child') as C(X)
  cross apply C.X.nodes('grandchild') as GC(X)
  cross apply GC.X.nodes('greatgrandchild') as GGC(X)
  cross apply GGC.X.nodes('parameter') as P(X)
where P.X.value('(value/text())[1]', 'varchar(max)') like '%[^Aa]BC%'

更新:

我冒昧地执行wBob在SQL Server 2014上提供的测试装备,兼容级别为110(SQL Server 2012)和120(SQL Server 2014)

结果:

Compatibility level  OP's query  My query  wBob query using XML indexes
-------------------  ----------  --------  ----------------------------
110                  64 sec      37 sec    1 sec
120                  8 sec       4 sec     5 sec

您在执行时间中看到的差异是因为SQL Server 2014使用新的基数估算器.当兼容级别为110时,SQL Server将使用旧的估算器.使用跟踪标志打开或关闭新的基数估算器会产生完全相同的结果.

不使用XML索引的查询的时间差异是因为在兼容性级别120中,计划与我的情况下的DOP 16并行.

值得注意的是,使用新的基数估算器时,使用XML索引的执行时间要慢五倍.其原因与上述相同,只是相反.只有在使用旧的基数估算器时才有并行计划.

转载注明原文:sql-server – 可以优化涉及XQuery函数的SELECT查询,从具有任意数量的子元素的元素中进行选择 - 代码日志