使用SQL获取Sibling节点

时间:2014-06-23 19:41:30

标签: sql sql-server sql-server-2008

我有一个XML字符串,如下所示

<Message>
  <Result>A</Result>
  <Note>AA</Note>
  <Note>AAA</Note>
  <Note>AAAA</Note>
  <Result>B</Result>
  <Note>BB</Note>
  <Note>BBB</Note>
  <Note>BBB</Note>
</Message>

我想使用SQL选择每个Note节点的3个Result节点,并将它们相互附加。我是怎么做到的?

For Result A -> AA+AAA+AAAA
For Result B -> BB+BBB+BBBB

1 个答案:

答案 0 :(得分:1)

假设您有一个如下所示的XML:

<Item Result="A">
  <Note>AA</Note>
  <Note>AAA</Note>
  <Note>AAAA</Note>
</Item>
<Item Result="B">
  <Note>BB</Note>
  <Note>BBB</Note>
  <Note>BBB</Note>
</Item>

然后通过粉碎Item并使用for xml path('')连接Note节点,可以轻松生成所需的结果。

select T.X.value('@Result', 'nvarchar(100)') as Result,
       (
       select T2.X.value('text()[1]', 'nvarchar(100)')+' '
       from T.X.nodes('Note') T2(X)
       for xml path(''), type
       ).value('text()[1]', 'nvarchar(100)') as Note
from @XML.nodes('/Item') as T(X);

您可以使用此查询将XML重建为所需的格式。

set @XML =  @XML.query('for $n in /Message/Result
                        return element Item {
                                            attribute Result{$n/text()},
                                            /Message/Note[. >> $n][position() < 4]
                                            }');

SQL Fiddle

另一种选择是在Message/*上粉碎并使用CTE中的row_number()枚举所有行。在主查询和使用for xml path('')的子查询中使用CTE将字符串与使用生成的数字的连接连接起来。

with C as 
(
  select T.X.value('text()[1]', 'nvarchar(100)') as Value,
         T.X.value('local-name(.)', 'nvarchar(100)') as NodeName,
         row_number() over(order by T.X)as  rn
  from @XML.nodes('/Message/*') as T(X)
)
select C1.Value as Result,
       (
       select C2.Value+' '
       from C as C2
       where C2.rn > C1.rn and
             C2.rn < C1.rn + 4
       for xml path(''), type
       ).value('text()[1]', 'nvarchar(100)') as Note
from C as C1
where C1.NodeName = 'Result'

SQL Fiddle

<子> 注意:使用row_number()枚举XML文档中的节点的技术依赖于SQL Server分析XML的内部实现的知识。 Microsoft未记录该行为。 Adam Machanic在此发表了博文:Uniquely Identifying XML Nodes with DENSE_RANK