可以优化涉及 XQuery 函数的 SELECT 查询,该查询从具有任意数量子元素的元素中选择

cub*_*729 2 xml sql-server sql-server-2012 xquery

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

  • <root>可以有任意数量的<child>孩子
  • <child>可以有任意数量的<grandchild>孩子
  • <grandchild>可以有任意数量的<greatgrandchild>孩子
  • <child>并且<grandchild>可以有任意数量的<parameter>孩子
  • 每个人<parameter>都有<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>
Run Code Online (Sandbox Code Playgroud)

我需要做的是选择包含“_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%'
Run Code Online (Sandbox Code Playgroud)

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

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>');
Run Code Online (Sandbox Code Playgroud)

应返回以下结果集:

TestId root child grandchild greatgrandchild parameter
------------------------------------------------------
1      root a     a1         NULL            param2
1      root a     a1         a1a             paramB
Run Code Online (Sandbox Code Playgroud)

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

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

Mik*_*son 5

这是对您已有内容的重写。

  1. 在根节点上不需要粉碎。
  2. 指定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%'
Run Code Online (Sandbox Code Playgroud)

更新:

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

结果:

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
Run Code Online (Sandbox Code Playgroud)

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

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

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