简体   繁体   English

如何在SQL Server中将复杂的XML解析为多个插入和更新?

[英]How to parse complex XML to multiple inserts and updates in SQL server?

I have found examples of parsing xml to inserts. 我发现了将xml解析为插入的示例。 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. 但是我有与此类似的XML-在这里我需要在其他表中插入子元素。

<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. 我需要遍历此xml,首先将一行插入表“ Families”中,然后返回该家庭的ID,并在下一个INSERT中将其用作表“ Persons”中某个人的外键,并与嗜好相同。 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. 将不胜感激。

Unfortunately, SQL Server doesn't support multi-table insert, so you would need to do single inserts like this: 不幸的是,SQL Server不支持多表插入,因此您需要执行以下单个插入:

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

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). 我的解决方案考虑到了这一“方面”(注意:我使用了经过修改的XML进行测试)。 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). RowNum(和FamilyID)。 The tricky part is in the last CROSS APPLY that extracts all /person elements for every family name occurrence: 棘手的部分是在最后一个CROSS APPLY ,它为每个姓氏出现都提取了所有/person元素:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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