一尘不染

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

sql

我想将记录从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

谢谢!


阅读 123

收藏
2021-05-16

共1个答案

一尘不染

您需要保持相关性,下面将向您展示如何做。我还通过添加很多灵活性来使其更加健壮。

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
2021-05-16