在SQL中将XML切成多行

时间:2019-04-16 18:11:43

标签: sql-server xml tsql sql-server-2012 xml-parsing

我必须每两周切碎一个XML文档。它符合NEMSIS 2标准,并且我有一个符合标准的数据库来保存信息。我已经成功构建了一个查询来导入所有一对一数据,但是我在切碎一对多数据时遇到了麻烦。

使用此演示数据:

declare @x xml  
set @x =  
'<EMSDataSet>  
    <Header>  
    <D01_01>abc</D01_01>  
        <Record>  
            <E01>  
                <E01_01>12345</E01_01>  
                <E01_02>678</E01_02>  
            </E01>  
            <E02>  
                <E02_01>123</E02_01>  
                <E02_09>295</E02_09>  
                <E02_09>296</E02_09>  
            </E02>  
        </Record>
        <Record>
            <E01>
                <E01_01>67890</E01_01>
                <E01_02>678</E01_02>
            </E01>
            <E02>
                <E02_01>123</E02_01>
                <E02_09>295</E02_09>                
            </E02>
        </Record>  
    </Header>  
</EMSDataSet>'  

此查询正确返回正确的值01_01和第一个值02_09。但是,由于 Value()只能返回一个单例,因此我只能获得第一个值:

Query:  
select  
    t.c.value('(E01/E01_01)[1]','varchar(max)') e01_01  
   ,t.c.value('(E02/E02_09)[1]','varchar(max)') e02_09  
from @x.nodes('EMSDataSet/Header/Record') t(c)  

Returns:  
e01_01     e02_09  
------     ------  
 12345        295  

此查询在其各自的行中返回两个值02_09的值,但没有对应的值01_01的值:

Query:  
select  
    t2.c.value('.','varchar(max)') e02_09  
from @x.nodes('EMSDataSet/Header/Record/E02/E02_09') t2(c)  

Returns:  
e02_09  
 -----  
   295  
   296  

我需要完成的是合并结果,并将值02_09的两个值与在每一行上重复的值01_01合并。值01_01将充当数据库中的外键,引用特定的唯一事件。

e01_01     e02_09  
------     ------  
 12345        295  
 12345        296  

它似乎可能需要使用 Nodes()方法,可能还需要使用 Join ,但是我无法弄清楚语法来获取单一值01_01的值对每个值02_09的值重复此操作。

这些问题/答案到目前为止已经使我明白了:

  

Retrieve-all-child-nodes-from-a-parent-node-xml-sql-server
  Returning multiple rows from querying XML column in SQL Server 2008
  T-SQL Shred Second Level XML Nodes into Multiple Rows
  Shred XML For Each Row in SQL Table

编辑: 在早期取得成功之后,我尝试遵循代码。但是,它没有从我的测试数据集中返回正确的36行(35条记录,重复两次e02_09的重复值),而是返回了1260条记录(36行* 35行)。

select *
from
    (
      select 
          t.c.value('(E01/E01_01)[1]','varchar(max)') e01_01
      from @x.nodes('EMSDataSet/Header/Record') t(c)
    ) a
    ,
    (
      select
          t2.c.value('.','varchar(max)') e02_09
      from @x.nodes('EMSDataSet/Header/Record/E02/E02_09') t2(c)
    ) b

1 个答案:

答案 0 :(得分:0)

我能够实现的最佳答案是基于以下blog post by Adam Machanic

select 
    e01_01
    ,e02_09
from
    (select 
        dense_rank() over (order by b_node) unique_b_node
        ,c_node.value('./text()[1]','varchar(max)') e02_09

    from @x.nodes('/EMSDataSet/Header/Record') b(b_node)
    cross apply b.b_node.nodes('./E02/E02_09') c(c_node)
    ) a
join
    (select
        dense_rank() over (order by b2.b_node) unique_b_node
        ,c_node.value('text()[1]','varchar(max)') e01_01
    from @x.nodes('/EMSDataSet/Header/Record') b2(b_node)
    cross apply b2.b_node.nodes('./E01/E01_01') c2(c_node)
    ) b
on a.unique_b_node = b.unique_b_node