从XML维护关系插入两个SQL表

时间:2011-01-14 23:49:29

标签: sql xml

我希望将xml中的记录插入到两个不同的表中。 例如

<Root>
    <A>
        <AValue>value</AValue>
        <Children>
            <B>
                <BValue>2</BValue>
            </B>
        </Children>
    </A>
    <A>
        <AValue>value</AValue>
        <Children>
            <B>
                <BValue>3</BValue>
            </B>
        </Children>
    </A>
</Root>

将记录插入表A中 假设身份从1开始

AID  AValue
1    value
2    value

还将记录插入表B

BID AID  BValue
1   1    2
2   2    3

我有这个

DECLARE @idoc INT
DECLARE @doc NVARCHAR(MAX)

SET @doc = '
<Root>
    <A>
        <AValue>value</AValue>
        <Children>
            <B>
                <BValue>2</BValue>
            </B>
        </Children>
    </A>
     <A>
        <AValue>value</AValue>
        <Children>
            <B>
                <BValue>3</BValue>
            </B>
        </Children>
    </A>
</Root>
'

EXEC sp_xml_preparedocument @idoc OUTPUT, @doc

CREATE TABLE #A
    (
      AID INT IDENTITY(1, 1) ,
      AValue varchar(100)
    )

INSERT  INTO #A
        SELECT  *
        FROM    OPENXML (@idoc, '/Root/A',2)
                WITH (AValue varchar(100)
                      )  

CREATE TABLE #B
    (
      BID INT IDENTITY(1, 1) ,
      AID INT ,
      BValue INT
    )

INSERT  INTO #B
        SELECT  *
        FROM    OPENXML (@idoc, '/Root/A/Children/B',2)
                WITH (
                AID INT,
                BValue INT
                      )

SELECT  *
FROM    #A
SELECT  *
FROM    #B

DROP TABLE #A
DROP TABLE #B
exec sp_xml_removedocument @idoc

谢谢!

2 个答案:

答案 0 :(得分:2)

您需要保持相关性,下面将告诉您如何操作。我还通过增加一些灵活性使其更加强大。

DECLARE @A table
    (
-- start with non-1, to show the solution is not dependent on starting at 1
      AID INT IDENTITY(14, 1) ,
      AValue varchar(100)
    )
DECLARE @B TABLE
    (
      BID INT IDENTITY(1, 1) ,
      AID INT ,
      BValue INT
    )

DECLARE @xml XML

-- allow for duplicate values on A.AValue
-- allow for multiple B nodes
-- allow for A nodes without B children
SET @xml = '
<Root>
    <A>
        <AValue>value2</AValue>
        <Children>
            <B>
                <BValue>2</BValue>
            </B>
            <B>
                <BValue>4</BValue>
            </B>
        </Children>
    </A>
     <A>
        <AValue>value1</AValue>
        <Children>
            <B>
                <BValue>3</BValue>
            </B>
        </Children>
    </A>
     <A>
        <AValue>value1</AValue>
        <Children>
            <B>
                <BValue>9</BValue>
            </B>
        </Children>
    </A>
     <A>
        <AValue>valueX</AValue>
    </A>
</Root>
'

-- dump the data into a temp table for correlating A and B entries
-- since an A can have multiple B children, ARow identifies which A it really is,
--    multiple A records can have the same AValue

declare @tmp table (ARow int, avalue varchar(100), bvalue int)
INSERT @tmp (ARow, avalue, bvalue)
SELECT X.N, X.C.value('A[1]/AValue[1]','varchar(100)'), Y.V
FROM (
    SELECT T.C.query('.') C, row_number() over (order by C) N
    FROM @xml.nodes('//A') T(C)) X
OUTER APPLY (
    SELECT T2.C2.value('.','int') V
    FROM X.C.nodes('A[1]/Children/B/BValue') T2(C2)) Y

-- uncomment next line to see what has gone into @tmp
-- select * from @tmp

insert @A
select AValue
from (
    select distinct ARow, AValue
    from @tmp) X
order by ARow -- order by is important to maintain correlation

-- get the last identity generated to correlate AID in B
declare @lastid int
SET @lastid = scope_identity()

-- Max(ARow) is how many A records were entered, add back ARow to get
-- the ID generated for the A record

insert @B (AID, BValue)
select @lastid-M.M+ARow, BValue
from @tmp, (select max(ARow) M from @tmp) M
where BValue is not null
order by ARow

-- check results
select * from @A
select * from @B

答案 1 :(得分:1)

过程sp_xml_preparedocument有点旧,与内存泄漏有关。如果可以,最好避免使用它。

这是一种使用较新的xml data type抓取您寻找的数据的方法:

declare @doc xml
SET @doc = '...yourxml...'

create table #A (AID int, AValue varchar(50))
create table #B (BID int identity(1,1), AID int, BValue int)
create table #C (AID int, AValue varchar(50), BValue int)

insert  #C
select  *
from    (
        select  ROW_NUMBER() over (order by 
                    b.value('AValue[1]', 'varchar(50)')) as aid
        ,       b.value('AValue[1]', 'varchar(50)')
        ,       g.h
        from    @doc.nodes('/Root/A') a(b)
        cross apply
                (
                select  f.value('BValue[1]', 'int')
                from    b.nodes('Children/B') e(f)
                ) g(h)
        ) b(aid, avalue, bvalue)

insert #A select distinct AID, AValue from #C
insert #B (AID, BValue) select AID, BValue from #C

正如您所看到的,SQL Server中的XML支持相当复杂。如果可能,解析XML客户端。