简体   繁体   中英

Bulk insert in parent and child table using sp_xml_preparedocument

I am using sp_xml_preparedocument for bulk insertion. But I want to do bulk insert in parent table, get scope_identity for each newly inserted row and then bulk insert in child table.

I can do this by taking table variable for parent table in procedure and insert data in that table which I supposed to insert in parent table. Now loop through each row in cursor, insert in actual table and then in child table.

But is there any batter way without cursor? I want some optimum solution

If you are on SQL Server 2008 or later you can use merge as described in this question .

Create a table variable that will capture the generated id from the parent table along with the child XML when doing a merge against Parent . Then insert into Child from the table variable.

Note: I use the XML datatype instead of openxml.

Tables:

create table Parent
(
  ParentID int identity primary key,
  Name varchar(10) not null
);

create table Child
(
  ChildID int identity primary key,
  Name varchar(10) not null,
  ParentID int not null references Parent(ParentID)
);

XML:

<root>
  <parent>
    <name>parent 1</name>
    <child>
      <name>child 1</name>
    </child>
    <child>
      <name>child 2</name>
    </child>
  </parent>
  <parent>
    <name>parent 2</name>
    <child>
      <name>child 3</name>
    </child>
  </parent>
</root>

Code:

declare @Child table
(
  ParentID int primary key,
  Child xml
);

merge Parent as P
using (
      select T.X.value('(name/text())[1]', 'varchar(10)') as Name,
             T.X.query('child') as Child
      from @XML.nodes('/root/parent') as T(X)
      ) as X
on 0 = 1
when not matched then
  insert (Name) values (X.Name)
output inserted.ParentID, X.Child into @Child;

insert into Child(Name, ParentID)
select T.X.value('(name/text())[1]', 'varchar(max)'),
       C.ParentID
from @Child as C
  cross apply C.Child.nodes('/child') as T(X);

SQL Fiddle

Always, because cursors are evil!

I am assuming that you have some way to link the child records to the parent from the source file. I'm also assuming that you are bulk inserting from an XML file, where the child table is represented as child nodes in the XML? If you can give us an example of the XML you're trying to process, it would be a great help. For now, let's assume your XML looks a bit like:

<root>
    <parent>
        <num>1</num>
        <name>P1</name>
        <children>
            <child>
                <num>1</num>
                <name>C1</name>
            </child>
            ...

Your parent table will include a parent_key :

DECLARE @parent TABLE (
    num INT,
    parent_name VARCHAR(100),
    parent_key INT
)

and child table will have it's own fields, plus the identifying element from the parent:

DECLARE @child TABLE (
    num INT,
    child_name VARCHAR(100),
    parent_num INT
)

The parent table is populated, which is easy enough, so I'm not going to include an example of that. Leave the parent_key field as NULL, so you only populate the values you know.

The child table is populated with

INSERT INTO @child
SELECT * FROM OpenXML(@iDoc, '/root/parent/children/child', 2) WITH (
    num INT,
    name VARCHAR(100),
    parent_num INT AS '../../num'
)

Insert into your parent table, as normal:

INSERT INTO #parent (num, parent_name)
SELECT num, parent_name FROM @parent ORDER BY num

Then, using ROW_NUMBER and @@IDENTITY, you can derive the identity values used for the parent primary keys.

;WITH cte1 AS (SELECT COUNT(*) AS cnt FROM @parent),
cte2 AS (SELECT ROW_NUMBER() OVER (ORDER BY num) AS row_num, num FROM #parent)
UPDATE p SET parent_key = @@IDENTITY - cte1.cnt + cte2.row_num
FROM @parent p, cte1, cte2
WHERE p.num = cte2.num

From there, you should be okay.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM