将 XML 多个嵌套节点插入 SQL 表

Posted

技术标签:

【中文标题】将 XML 多个嵌套节点插入 SQL 表【英文标题】:Inserting XML Multiple Nested Nodes into SQL Tables 【发布时间】:2020-10-26 13:04:20 【问题描述】:

我有一个具有以下结构的 XML 文件:

 <NIACList>
    <NIAC>
            <Number></Number>
            <SubmissionDate></SubmissionDate>
            <ExpirationDate  />
            <IssuerIDNO></IssuerIDNO>
            <IssuerName></IssuerName>
            <SuspensionPeriod/>
            <Cessation>
              <Basis  />
              <Date  />
            </Cessation>
            <Merchant>
              <IDNx></IDNx>
              <Name></Name>
              <Address>
                <Region></Region>
                <Locality></Locality>
                <Street></Street>
                <House></House>
                <Block  />
                <Flat  />
                <Phone  />
                <Fax  />
                <Email  />
              </Address>
            </Merchant>
            <CommercialUnit>
              <IDNx  />
              <Name  />
              <Type></Type>
              <Area></Area>
              <Location></Location>
              <Address>
                <Region></Region>
                <Locality></Locality>
                <Street></Street>
                <House></House>
                <Block  />
                <Flat  />
              </Address>
              <Activities>
                <Activity>
                  <Code></Code>
                  <Name></Name>
                </Activity>
              </Activities>
              <Goods>
                <Good>
                  <Name></Name>
                </Good>
              </Goods>
              <WorkProgram  />
              <PublicSupplyUnit>
                <Capacity  />
                <TerraceCapacity  />
              </PublicSupplyUnit>
              <TradingAlcohol  />
              <TradingBeer  />
              <TradingTobaccoProducts  />
              <AmbulatoryTrading  />
              <MobileUnitTrading></MobileUnitTrading>
              <MobileUnit>
                <Type  />
                <Length  />
                <Width  />
                <Height  />
              </MobileUnit>
              <CommercialApparatusTrading></CommercialApparatusTrading>
              <CommercialApparatus>
                <Count  />
                <Length  />
                <Width  />
                <Height  />
              </CommercialApparatus>
            </CommercialUnit>
            <Modifications  />
          </NIAC>
    </NIACList>

基于这个 XML 结构,我在 MS SQL 中创建了一个带有以下表脚本的数据库:

create table NIAC (
IDNIAC int identity primary key,
Number nvarchar(80) null,
SubmissionDate datetime2 null,
ExpirationDate datetime2 null,
IssuerIDNO  nvarchar(max) null,
IssuerName  nvarchar(60) null,
SuspensionPeriod datetime2 null,
Modifications nvarchar(60) null
);

create table Cessation (
IdCessation int identity primary key,
Basis nvarchar(60) null,
Date date null,
IDNIAC int 
);


create table Merchant (
IdMerchant int identity  primary key,
IDNX nvarchar(max) null,
Name nvarchar(max) null,
WorkProgram datetime2 null,
IdAddress int 
);

create table Address (
IdAddress int identity  primary key,
Region nvarchar(60) null,
Locality nvarchar(50) null,
Street nvarchar (60) null,
House nvarchar (10) null,
Block nvarchar (10) null,
Flat nvarchar(10) null,
Phone nvarchar(30) null,
Fax nvarchar(60) null,
Email nvarchar(60) null

);

create table CommercialUnit (
IDCommercialUt int identity primary key,
IDNx nvarchar(90) null,
Name nvarchar(90) null,
Type nvarchar(90) null,
Area int null,
Location nvarchar(max) null,
TerraceCapacity float null,
TradingAlcohol bit null,
TradingBeer bit null,
TradingTobaccoProducts bit null,
AmbulatoryTrading bit null,
MobileUnitTrading bit null,
CommercialApparatusTrading bit null,
IdAddress int,
IDActivities int ,
IDGoods int ,
IDMobileUnit int ,
IDCommercial int ,
IDPSU int 
);


create table Activities (
IDActivities int identity  primary key,
Code nvarchar (90) null,
Name nvarchar (90) null
);

create table Goods(
IDGoods int identity  primary key,
Name nvarchar(60) null
);

create table MobileUnit(
IDMobileUnit int identity  primary key,
Type numeric null,
Length numeric null,
Width numeric null,
Height numeric null
);

create table CommercialApparatus(
IDCommercial int identity primary key,
Count float null,
Length numeric null,
Width numeric null,
Height numeric null
);


create table PublicSupplyUnit(
IDPSU int identity  primary key,
Capacity float,
TerraceCapacity float 
);


create table NIACList(
ID int identity primary key,
IDNIAC int not null,
IDNIACList int 
);


create table NIACToComponents(
IDNIACToComponents int identity primary key,
IDNIAC int,
IDAddressMerchant int,
IDAddressCU int,
IdCessation int,
IdMerchant int,
IDCommercialUt int,
IDActivities int,
IDGoods int,
IDMobileUnit int,
IDCommercial int,
IDPSU int,

);




drop table if exists NIAC;

drop table if exists NIACList;

drop table if exists Cessation;

drop table if exists Merchant;

drop table if exists Address;

drop table if exists CommercialApparatus;

drop table if exists CommercialUnit;

drop table if exists Goods;

drop table if exists MobileUnit;

drop table if exists  PublicSupplyUnit;

drop table if exists  Activities;

drop table if exists NIACToComponents;



alter table dbo.NIACList
add constraint FK_ID_NIAC 
foreign key (IDNIAC) references dbo.NIAC(IDNIAC)
on delete cascade
on update cascade;

alter table dbo.Cessation
Add constraint FK_IDNIAC
foreign key(IDNIAC) references dbo.NIAC(IDNIAC)
on delete cascade
on update cascade;

alter table dbo.Merchant
add constraint FK_IdAdress
foreign key(IdAddress) references dbo.Address(IdAddress)
on delete cascade
on update cascade;

alter table dbo.CommercialUnit
add constraint FK_IdAddress
foreign key(IdAddress) references dbo.Address(IdAddress)
on delete cascade
on update cascade;


alter table dbo.CommercialUnit
add constraint FK_Activities
foreign key(IDActivities) references dbo.Activities(IDActivities)
on delete cascade
on update cascade;

alter table dbo.CommercialUnit
add constraint FK_IDGoods
foreign key(IDGoods) references dbo.Goods(IDGoods)
on delete cascade
on update cascade;

alter table dbo.CommercialUnit
add constraint FK_IDMobileUnit
foreign key(IDMobileUnit) references dbo.MobileUnit(IDMobileUnit)
on delete cascade
on update cascade;

alter table dbo.CommercialUnit
add constraint FK_IDCommercial 
foreign key(IDCommercial) references dbo.CommercialApparatus(IDCommercial)
on delete cascade
on update cascade;

alter table dbo.CommercialUnit
add constraint FK_IDPSU
foreign key(IDPSU) references dbo.PublicSupplyUnit(IDPSU)
on delete cascade
on update cascade;

假设NIACList(50-100 个节点)中有很多 NIAC,我想从我的 XML 文件中插入数据。 我只为一个NIAC 执行了INSERT 操作,使用3 个过程(2 个用于插入表,1 个用于调用两个过程并将插入数据的表的所有ID 插入另一个表,所以所有数据都将链接到 NIAC)。

向表中插入数据的脚本如下: 1个程序:

ALTER procedure [dbo].[InsertXMLMultipleTables]
(
@idAddress1 int out,
@idAddress2 int out,
@idactivities int out,
@idgoods int out,
@idmobileunit int out,
@idcommercial int out,
@idcommercialut int out,
@idpsu int out,
@idmerchant int out,
@xml xml
)
as 
set @xml = *


begin 
set nocount on

INSERT INTO Activities(Code,Name)
        SELECT  
    
Code=c.value('Code[1]','nvarchar(90)') ,
Name=c.value('Name[1]','nvarchar(90)') 
FROM @xml.nodes('/NIACList/NIAC/CommercialUnit/Activities/Activity') Activities(c)
set @IDActivities=SCOPE_IDENTITY();
end;




begin
INSERT INTO Address(Region,Locality,Street,House,Block,Flat,Phone,Fax,Email)
        SELECT 
        Region=c.value('Region[1],','nvarchar(60)'),
        Locality=c.value('Locality[1],','nvarchar(50)') ,
        Street=c.value('Street[1],','nvarchar(60)') ,
        House=c.value('House[1],','nvarchar(10)') ,
        Block=c.value('Block[1],','nvarchar(10)') ,
        Flat=c.value('Flat[1],','nvarchar(10)') ,
        Phone=c.value('Phone[1],','nvarchar(30)') ,
        Fax=c.value('Fax[1],','nvarchar(60)') ,
        Email=c.value('Email[1],','nvarchar(60)') 
    FROM @xml.nodes('NIACList/NIAC/Merchant/Address') Address(c)
    set @idAddress1=SCOPE_IDENTITY();
    end;

    begin
    INSERT INTO Merchant(IDNX,Name,WorkProgram,IdAddress)
        SELECT 
        Region=c.value('IDNx[1],','nvarchar(max)'),
        Locality=c.value('Name[1],','nvarchar(max)') ,
        Street=c.value('WorkProgram[1],','datetime2') ,
        @idAddress1
    FROM @xml.nodes('NIACList/NIAC/Merchant') Merchant(c)
    set @idmerchant=SCOPE_IDENTITY();
    end;

    
begin
INSERT INTO Address(Region,Locality,Street,House,Block,Flat,Phone,Fax,Email)
        SELECT 
        Region=c.value('Region[1],','nvarchar(60)'),
        Locality=c.value('Locality[1],','nvarchar(50)') ,
        Street=c.value('Street[1],','nvarchar(60)') ,
        House=c.value('House[1],','nvarchar(10)') ,
        Block=c.value('Block[1],','nvarchar(10)') ,
        Flat=c.value('Flat[1],','nvarchar(10)') ,
        Phone=c.value('Phone[1],','nvarchar(30)') ,
        Fax=c.value('Fax[1],','nvarchar(60)') ,
        Email=c.value('Email[1],','nvarchar(60)')   
    FROM @xml.nodes('NIACList/NIAC/CommercialUnit/Address') Address(c)
    set @idAddress2=SCOPE_IDENTITY();
    set @idcommercialut=SCOPE_IDENTITY();

    end;


    begin
    INSERT INTO CommercialApparatus(Count,Length,Width,Height )
        SELECT      
    Count =c.value('Type[1]','int') ,
        Length=c.value('Length[1]','int') ,
         Width=c.value('Width[1]','int') ,
         Height=c.value('Height[1]','int') 
    FROM @xml.nodes('/NIACList/NIAC/CommercialUnit/CommercialApparatus') CommercialApparatus(c)
    set @idcommercial=SCOPE_IDENTITY();
    end;




    begin
    INSERT INTO Goods(Name)
        SELECT 
        Name=c.value('Name[1]','nvarchar(60)') 
    FROM @xml.nodes('/NIACList/NIAC/CommercialUnit/Goods/Good') Goods(c)
    set @idgoods=SCOPE_IDENTITY();
    end;

    begin
    INSERT INTO MobileUnit(Type,Length,Width,Height )
        SELECT  
    Type =c.value('Type[1]','int') ,
        Length=c.value('Length[1]','int') ,
         Width=c.value('Width[1]','int') ,
         Height=c.value('Height[1]','int') 
    FROM @xml.nodes('/NIACList/NIAC/CommercialUnit/MobileUnit') MobileUnit(c)
         set @idmobileunit=SCOPE_IDENTITY();
    end;



    begin
    INSERT INTO PublicSupplyUnit(Capacity,TerraceCapacity)
        SELECT 
        Capacity=c.value('Capacity[1]','int') ,
        TerraceCapacity=c.value('TerraceCapacity[1]','int')
    FROM @xml.nodes('/NIACList/NIAC/CommercialUnit/PublicSupplyUnit') PublicSupplyUnit(c)
    set @idpsu=SCOPE_IDENTITY();
    end;



    begin
    INSERT INTO CommercialUnit(IDNX,Name,Type,Area,Location,TerraceCapacity,TradingAlcohol,TradingBeer,TradingTobaccoProducts,AmbulatoryTrading,MobileUnitTrading,CommercialApparatusTrading,IDActivities,IdAddress,IDCommercial,IDGoods,IDMobileUnit,IDPSU)
        SELECT 
        IDNx=c.value('IDNx[1],','nvarchar(90)'),
        Name=c.value('Name[1],','nvarchar(90)') ,
        Type=c.value('Type[1],','nvarchar(90)') ,
        Area=c.value('Area[1],','int') ,
        Location=c.value('Location[1],','nvarchar(max)'),
        TerraceCapacity=c.value('TerraceCapacity[1],','float') ,
        TradingAlcohol=c.value('TradingAlcohol[1],','bit') ,
        TradingBeer=c.value('TradingBeer[1],','bit') ,
        TradingTobaccoProducts=c.value('TradingTobaccoProducts[1],','bit'),
        AmbulatoryTrading=c.value('AmbulatoryTrading[1],','bit') ,
        MobileUnitTrading=c.value('MobileUnitTrading[1],','bit') ,
        CommercialApparatusTrading=c.value('CommercialApparatusTrading[1],','bit') ,
        @idactivities,@idAddress2,@idcommercial,@idgoods,@idmobileunit,@idpsu
    FROM @xml.nodes('NIACList/NIAC/CommercialUnit') CommercialUnit(c)
    end;

2 程序:

ALTER procedure [dbo].[InsertXMLNIAC]
(
@xml xml,
@idNIAC int output,
@idCessation int output
)
as 
set @xml=*

begin 
set nocount on


INSERT INTO NIAC(Number,SubmissionDate,ExpirationDate,IssuerIDNO,IssuerName,SuspensionPeriod,Modifications)
        SELECT 
        Number=c.value('Number[1],','nvarchar(80)'),
        SubmissionDate=c.value('SubmissionDate[1],','datetime2') ,
        ExpirationDate=c.value('ExpirationDate[1],','datetime2') ,
        IssuerIDNO=c.value('IssuerIDNO[1],','nvarchar(max)') ,
        IssuerName=c.value('IssuerName[1],','nvarchar(60)') ,
        SuspensionPeriod=c.value('SuspensionPeriod[1],','datetime2') ,
        Modifications=c.value('Modifications[1],','nvarchar(60)') 
    FROM @xml.nodes('NIACList/NIAC') NIAC(c)
    set @idNIAC=SCOPE_IDENTITY();
    end;

    begin
    INSERT INTO Cessation(Basis,Date,IDNIAC)
        SELECT 
        Basis=c.value('Basis[1],','nvarchar(60)'),
        Date=c.value('Date[1],','date') ,      
        @idNIAC
    FROM @xml.nodes('NIACList/NIAC/Cessation') Cessation(c)
    set @idCessation=SCOPE_IDENTITY();
    end;

3 程序:

ALTER PROCEDURE [dbo].[NIACFORCOMPONENTS]
@xml1 xml
AS
BEGIN
DECLARE 
        @idAddress1 int,
        @idAddress2 int,
        @idactivities int,
        @idgoods int,
        @idmobileunit int,
        @idcommercial int,
        @idcommercialut int,
        @idpsu int,
        @idmerchant int,
        @idNIAC int,
        @idCessation int


    SET NOCOUNT ON;
    
    declare @trancount int;
    set @trancount=@@TRANCOUNT;
    BEGIN TRANSACTION

    EXEC [dbo].[InsertXMLMultipleTables]
        @idAddress1 = @idAddress1 OUTPUT,
        @idAddress2 = @idAddress2 OUTPUT,
        @IDActivities = @idactivities OUTPUT,
        @idgoods = @idgoods OUTPUT,
        @idmobileunit = @idmobileunit OUTPUT,
        @idcommercial = @idcommercial OUTPUT,
        @idcommercialut = @idcommercialut OUTPUT,
        @idpsu = @idpsu OUTPUT,
        @idmerchant = @idmerchant OUTPUT,
        @xml = @xml1


        EXEC [dbo].[InsertXMLNIAC]
        
        @idNIAC = @idNIAC OUTPUT,
        @idCessation = @idCessation OUTPUT,
        @xml=@xml1

     
        


         begin try
        if @trancount = 0
            begin transaction
        else
            save transaction NIACFORCOMPONENTS;

        

lbexit:
        if @trancount = 0   
            commit;
    end try
    begin catch
        declare @error int, @message varchar(4000), @xstate int;
        select @error = ERROR_NUMBER(),
                 @message = ERROR_MESSAGE(), @xstate = XACT_STATE();
        if @xstate = -1
            rollback;
        if @xstate = 1 and @trancount = 0
            rollback
        if @xstate = 1 and @trancount > 0
            rollback transaction NIACFORCOMPONENTS;

        raiserror ('NIACFORCOMPONENTS: %d: %s', 16, 1, @error, @message) ;
        return;
    end catch   
    
    Begin
                              
        INSERT INTO NIACToComponents(IDNIAC,IdCessation,IdMerchant,IDAddressMerchant,IDAddressCU,IDCommercialUt,IDActivities,IDGoods,IDMobileUnit,IDCommercial,IDPSU)
        SELECT 
        @idNIAC as N'@idNIAC',
        @idCessation as N'@idCessation',
        @idmerchant as N'@idmerchant',
        @idAddress1 as N'@idAddress1',
        @idAddress2 as N'@idAddress2',
        @idcommercialut as N'@idcommercialut',
        @idactivities as N'@idactivities',
        @idgoods as N'@idgoods',
        @idmobileunit as N'@idmobileunit',
        @idcommercial as N'@idcommercial',
        @idpsu as N'@idpsu'
        
        
    
    COMMIT
        END             
END

基于这些脚本,插入非常适合一个 NIAC,表中的每个 ID 都链接到它,但我想对 NIACList 内的所有 NIAC 节点进行此类插入,知道他们每个人的位置。

【问题讨论】:

我建议通过 SSIS 执行 XML 读取和 DB 插入。它工作得很好,尤其是当您创建一个 XSLT 表来为您进行 XML 解释时。 【参考方案1】:

您可以根据需要使用以下概念示例。

您可以通过遍历父 XML 元素将INSERT 数据写入数据库表。您将需要两个 INSERT 语句。在第一个之后,您需要保留一个 IDENTITY 值,就像您今天已经在做的那样,在第二个 INSERT 中用于子 XML 片段和相应的行。

SQL

DECLARE @INPUTXML XML = '<Lineitems>
  <Lineitem>
    <Ticker>TER</Ticker>
    <ID>0</ID>
    <LineItem>Net Revenue</LineItem>
    <XFundCode>TRIN</XFundCode>
    <UserID>TDP</UserID>
  </Lineitem>
  <Lineitem>
    <Ticker>TER</Ticker>
    <ID>0</ID>
    <LineItem>Cost of Revenue</LineItem>
    <XFundCode>XXP</XFundCode>
    <UserID>TDP</UserID>    
  </Lineitem>
</Lineitems>';

DECLARE @Ticker  AS VARCHAR(MAX)
   , @ID        AS INT
   , @Lineitem  AS VARCHAR(MAX)
   , @XFundCode AS VARCHAR(MAX)
   , @UserID    AS VARCHAR(MAX);

-- count total number of items
DECLARE @i INT
   , @cnt INT = @INPUTXML.value('count(/Lineitems/Lineitem)', 'INT');
SELECT @cnt;

-- loop XML item by item
SET @i = 1;
WHILE @i <= @cnt BEGIN

      SELECT @Ticker = col.value('(Ticker/text())[1]','VARCHAR(MAX)')
         , @ID = col.value('(ID/text())[1]','INT')
         , @Lineitem = col.value('(LineItem/text())[1]','VARCHAR(MAX)')
         , @XFundCode = col.value('(XFundCode/text())[1]','VARCHAR(MAX)')
         , @UserID = col.value('(UserID/text())[1]','VARCHAR(MAX)')
      FROM @INPUTXML.nodes('/Lineitems/Lineitem[position() = sql:variable("@i")]') AS tab(col)

     -- do whatever needed in the loop here

   SET @i += 1;
END

【讨论】:

markossw,很高兴听到建议的解决方案对您有用。请在 LinkedIn 上与我联系。

以上是关于将 XML 多个嵌套节点插入 SQL 表的主要内容,如果未能解决你的问题,请参考以下文章

将具有多个嵌套级别的任何 XML 读取到结构化表中以写入 Excel 的通用方法

如何使用pyspark从xml的每个嵌套节点创建一个表

Oracle SQL3 3 嵌套表插入问题

mysql中insert与select的嵌套使用解决组合字段插入问题

将 XML 数据插入到具有多个节点的 SQL 表中

XSLT - 如何将节点内的内联/转义 XML 视为嵌套节点