使用nodes()方法在SQL中展平分层XML

Mat*_*don 5 xml sql t-sql sql-server-2005 sql-server-2008

我有一个存储过程,它将XML文档作为一个类似于以下结构的参数:

<grandparent name="grandpa bob">
  <parent name="papa john">
    <children>
      <child name="mark" />
      <child name="cindy" />
    </children>
  </parent>
  <parent name="papa henry">
    <children>
      <child name="mary" />
    </children>
  </parent>
</grandparent>
Run Code Online (Sandbox Code Playgroud)

我的要求是"展平"这些数据,以便可以将其插入临时表并在程序中进一步操作,因此上面的XML变为:

Grandparent Name Parent Name     Child Name
---------------- --------------- ---------------
grandpa bob      papa john       mark
grandpa bob      papa john       cindy
grandpa bob      papa henry      mary
Run Code Online (Sandbox Code Playgroud)

目前正在使用SQL Server XML节点完成此操作:

SELECT
    VIRT.node.value('../../../@name','varchar(15)') 'Grandparent Name',
    VIRT.node.value('../../@name','varchar(15)') 'Parent Name',
    VIRT.node.value('@name','varchar(15)') 'Child Name'
FROM
    @xmlFamilyTree.nodes('/grandparent/parent/children/child') AS VIRT(node)
Run Code Online (Sandbox Code Playgroud)

这很有效,直到我开始在程序中抛出大量数据(即1000多个child节点),此时这会停止并需要1到2分钟才能执行.我想这可能是因为我从最低级别(<child)开始,然后为每次出现遍历XML文档.将这个单个查询拆分为3个块(每个节点需要一个数据)可以提高性能吗?鉴于这些节点中没有一个节点上有"键",我可以用来连接备份,有人可以提供任何指示我可以如何做到这一点吗?

Mat*_*don 7

经过一番网上搜索后,我似乎回答了自己的问题:

SELECT
    grandparent.gname.value('@name', 'VARCHAR(15)'),
    parent.pname.value('@name', 'VARCHAR(15)'),
    child.cname.value('@name', 'VARCHAR(15)')
FROM
    @xmlFamilyTree.nodes('/grandparent') AS grandparent(gname)
CROSS APPLY
    grandparent.gname.nodes('*') AS parent(pname)
CROSS APPLY
    parent.pname.nodes('children/*') AS child(cname)
Run Code Online (Sandbox Code Playgroud)

使用CROSS APPLY我可以选择顶级grandparent节点并使用它来选择子parent节点,依此类推.使用这种方法,我已经将我的查询从大约1分30秒执行到大约6秒.

有趣的是,如果我使用"旧" OPEN XML方法来检索相同的数据,查询将在1秒内执行!

看起来您可能需要逐个使用这两种技术,具体取决于传入文档的预期大小/复杂程度.