使用t-sql将xml分解为表

时间:2012-07-25 12:22:39

标签: sql-server xml tsql

我有以下xml doc

<Root>
    <Translation>
        <Entry language ="ES"  text = "Alsace"/>
        <Entry language ="DE"  text = "Elsass"/>
        <Entry language ="EN" text = "Alsace"/>
    </Translation>
    <Translation>
        <Entry language ="ES"  text = "Brittany"/>
        <Entry language ="DE"  text = "Bretagne"/>
        <Entry language ="EN" text = "Brittany"/>
    </Translation>
</Root>

我需要将它切碎成一张桌子,同时保留这些团体。 因此,因为translation元素缺少任何id属性,所以分组隐含在结构中。

表格结果应如下所示。

translation_id, lanuage, text
---------------------------------
1,ES, Alsace 
1,DE,  Elsass 
1,EN, Alsace 
2,ES , Brittany 
2,DE, Bretagne 
2,EN Brittany

我尝试了各种各样的事情,比如

select newid(),
(   select rows.n.value('(@language)[1]', 'nvarchar(max)')
    from @p.nodes('/Translation/Entry') rows(n)
    )
from @p.nodes('/Translation') rows(n)

但似乎无法使用某种循环来获取分组ID。

由于

3 个答案:

答案 0 :(得分:2)

没有循环? OK ......

select 
    rn,
    t2.Entry.value('./@language','varchar(5)')  ,       
    t2.Entry.value('./@text','varchar(50)')         
    from
    (
        select 
            row_number() over (order by rows.n) as rn,
            rows.n.query('.') as trans  
            from @p.nodes('/Root/Translation') rows(n)
    ) trans     
        cross apply trans.nodes('Translation/Entry') as t2(Entry)

答案 1 :(得分:0)

最终解决了这个问题。不漂亮,我认为必须有一种方法可以在没有循环的情况下完成它。

declare @p xml

set @p = 
'<Translation>
<Entry language ="ES"  text = "Alsace"/>
<Entry language ="DE"  text = "Elsass"/>
<Entry language ="EN"  text = "Alsace"/>
</Translation>
<Translation>
<Entry language ="ES"  text = "Aquitaine"/>
<Entry language ="DE"  text = "Aquitanien"/>
<Entry language ="EN"  text = "Aquitaine"/>
</Translation>
'

declare @trans table (id int identity, t xml, [guid] varchar(255))
declare @result table ([guid] varchar(255), language char(2), [text] nvarchar(max) )
declare @no int , @i int, @guid varchar(255), @t xml

insert @trans
(t, [guid])
select rows.n.query('.') as trans, newid() id
from @p.nodes('/Translation') rows(n)
set @no = scope_identity()
set @i =1

while @i <= @no
begin

select @guid = [guid],
@t = t
from @trans
where id = @i

insert @result
([guid], language, [text] )
select @guid, 
rows.n.value('(@language)[1]', 'nvarchar(max)'),
rows.n.value('(@text)[1]', 'nvarchar(max)')
from @t.nodes('/Translation/Entry') rows(n)

set @i = @i +1

end


select * from @result

答案 2 :(得分:0)

我写这段代码,不是很好的工作,但它正在发挥作用。

        declare @p xml
    set @p = '<Root>
    <Translation>
    <Entry language ="ES"  text = "Alsace"/>
    <Entry language ="DE"  text = "Elsass"/>
    <Entry language ="EN" text = "Alsace"/>
    </Translation>
    <Translation>
    <Entry language ="ES"  text = "Brittany"/>
    <Entry language ="DE"  text = "Bretagne"/>
    <Entry language ="EN" text = "Brittany"/>
    </Translation>
    </Root>'

    Declare @n int
    set @n = 1  -- counter for transaction nodes in xml

    DECLARE @test xml -- to hold complete transaction node

    While @n <= 2 -- while loop starts
    BEGIN
    SET @test = ( select @p.query('/Root/Translation[sql:variable("@n")]') )

    SELECT
    @n as [Transaction],
    a.b.value('Entry[1]/@language','varchar(10)') AS [Language],
    a.b.value('Entry[1]/@text','varchar(10)') AS [Text]
    FROM @test.nodes('Translation') a(b)

    select @n as [Transaction] ,a.b.value('Entry[2]/@language','varchar(10)') AS [Language],
    a.b.value('Entry[2]/@text','varchar(10)') AS [Text]
    FROM @test.nodes('Translation') a(b)

    SELECT
    @n as [Transaction],
    a.b.value('Entry[3]/@language','varchar(10)') AS [Language],
    a.b.value('Entry[3]/@text','varchar(10)') AS [Text]
    FROM @test.nodes('Translation') a(b)


    SET @n = @n + 1  -- counter for transaction nodes in xml

    END  -- loop ended

Output:
    translation_id, lanuage, text
    ---------------------------------
    1,ES, Alsace 
    1,DE,  Elsass 
    1,EN, Alsace 
    2,ES , Brittany 
    2,DE, Bretagne 
    2,EN Brittany