1

I have found examples of parsing xml to inserts. However these examples are really simple. They are usually just like this:

<person>
    <name>Martin</name>
</person>
<person>
    <name>John</name>
</person>

But I have XML similar to this - Where I need to have inserts into other tables for child elements.

<root>
    <family>
        <name>Smith</name>
        <address>Some road 1</address>
        <persons>
            <person>
                <name>Tina</name>
                <hobbies>
                    <hobby>Some hobby 1</hobby>
                    <hobby>Some hobby 2</hobby>
                </hobbies>
            </person>
            <person>
                <name>Martin</name>
                <hobbies>
                    <hobby>Some hobby 1</hobby>
                    <hobby>Some hobby 2</hobby>
                </hobbies>
            </person>
        </persons>
    </family>
    <family>
        <name>Lane</name>
        <address>Some road 1</address>
        <persons>
            <person>
                <name>Kevin</name>
                <hobbies>
                    <hobby>Some hobby 1</hobby>
                    <hobby>Some hobby 2</hobby>
                </hobbies>
            </person>
            <person>
                <name>Julia</name>
                <hobbies>
                    <hobby>Some hobby 1</hobby>
                    <hobby>Some hobby 2</hobby>
                </hobbies>
            </person>
        </persons>
    </family>
</root>

I need to iterate through this xml and first INSERT a row into table "Families" After that I return the ID for the family and use it as foreign key in the next INSERT for a person in the table "Persons" and same with the hobbies. I think you get the idea. And after a "Family" I need to do some update statements before moving on to the next family.

Could someone point me in the right direction ? Would be much appreciated.

Bogdan Sahlean
  • 19,233
  • 3
  • 42
  • 57
Stefan
  • 13
  • 1
  • 3
  • which server side language are you using .. ? – alwaysLearn Apr 09 '13 at 07:35
  • Quite frankly it's not clear that you have done your homework as is expected in SO. This page easily found with Google shows two related tables being selected from the xml file: http://www.mssqltips.com/sqlservertip/2899/importing-and-processing-data-from-xml-files-into-sql-server-tables/. It seems you have to do multiple passes. – koriander Apr 09 '13 at 08:04
  • If you are on SQL Server 2008 or later you can use the technique from [this answer](http://stackoverflow.com/a/12853080/569436) – Mikael Eriksson Apr 09 '13 at 08:08

2 Answers2

2

Unfortunately, SQL Server doesn't support multi-table insert, so you would need to do single inserts like this:

insert into family  
    select f.node.value('name[1]', 'varchar(32)') as name
    from @xml.nodes('/root/family') f(node)

insert into person
    select family.ID as familyID, p.node.value('name[1]', 'varchar(32)') as name
    from @xml.nodes('/root/family') f(node)
    cross apply f.node.nodes('persons/person') p(node)
    inner join family on f.node.value('name[1]', 'varchar(32)') = family.name
muhmud
  • 4,474
  • 2
  • 15
  • 22
0

The main problem here is that you can have many families with the same name. My solution take into account this "aspect" (note: I used a modified XML for testing). In this example, you can see two families with the same name (Smith) but diff. persons. These families have diff. RowNum's (and FamilyID's). The tricky part is in the last CROSS APPLY that extracts all /person elements for every family name occurrence:

CROSS APPLY @x.nodes('/root/family[name=sql:column("f.Name")][sql:column("f.RowNum")]/persons/person') AS a(b)

Note: you can develop this solution to extract, also, every hobby.

DECLARE @x XML = N'
<root>
    <family>
        <name>Smith</name>
        <address>Some road 1</address>
        <persons>
            <person>
                <name>Tina</name>
                <hobbies>
                    <hobby>Some hobby 1</hobby>
                    <hobby>Some hobby 2</hobby>
                </hobbies>
            </person>
            <person>
                <name>Martin</name>
                <hobbies>
                    <hobby>Some hobby 1</hobby>
                    <hobby>Some hobby 2</hobby>
                </hobbies>
            </person>
        </persons>
    </family>
    <family>
        <name>Lane</name>
        <address>Some road 1</address>
        <persons>
            <person>
                <name>Kevin</name>
                <hobbies>
                    <hobby>Some hobby 1</hobby>
                    <hobby>Some hobby 2</hobby>
                </hobbies>
            </person>
            <person>
                <name>Julia</name>
                <hobbies>
                    <hobby>Some hobby 1</hobby>
                    <hobby>Some hobby 2</hobby>
                </hobbies>
            </person>
        </persons>
    </family>
    <family>
        <name>Smith</name>
        <address>Some another road 11</address>
        <persons>
            <person>
                <name>Coco</name>
            </person>
            <person>
                <name>Jambo</name>
            </person>
        </persons>
    </family>
</root>';

DECLARE @Family TABLE (
    FamilyID INT IDENTITY(1,1) UNIQUE,
    Name NVARCHAR(50) NOT NULL,
    RowNum INT NOT NULL,
    PRIMARY KEY (Name, RowNum)
);
INSERT  @Family (Name, RowNum)
SELECT  src.Name, 
        ROW_NUMBER() OVER(PARTITION BY src.Name ORDER BY @@SPID) AS RowNum
FROM (
    SELECT  a.b.value('(name)[1]', 'NVARCHAR(50)') AS Name
    FROM    @x.nodes('/root/family') AS a(b)
) src;

SELECT  f.*
FROM    @Family AS f

DECLARE @Person TABLE (
    PersonID INT IDENTITY(1,1) UNIQUE,
    FamilyID INT NOT NULL, -- Kind of FK
    Name NVARCHAR(50) NOT NULL,
    RowNum INT NOT NULL,
    PRIMARY KEY (Name, RowNum)  
)
INSERT  @Person (FamilyID, Name, RowNum)
SELECT  src.FamilyID, 
        src.Name,
        ROW_NUMBER() OVER(PARTITION BY src.FamilyID, src.Name ORDER BY @@SPID) AS RowNum
FROM (
    SELECT  f.FamilyID,
            a.b.value('(name)[1]', 'NVARCHAR(50)') AS Name
    FROM    @family f
    CROSS APPLY @x.nodes('/root/family[name=sql:column("f.Name")][sql:column("f.RowNum")]/persons/person') AS a(b)
) src;

SELECT  p.*
FROM    @Person AS p;

Results:

FamilyID Name  RowNum
-------- ----- ------
1        Lane  1
2        Smith 1
3        Smith 2

PersonID FamilyID Name   RowNum
-------- -------- ------ ------
5        3        Coco   1
6        3        Jambo  1
1        1        Julia  1
2        1        Kevin  1
3        2        Martin 1
4        2        Tina   1
Bogdan Sahlean
  • 19,233
  • 3
  • 42
  • 57