efficient way to transform XML document into tabular dataset in SQL because cross apply xml query performs exponentially worse as xml grows

一世执手 提交于 2020-05-30 20:57:35

问题


I have a big size XML document (50.000-100,000) that needs to be parsed on Azure SQL that looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<covid-19 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://covid-19.iss.it/XMLSchema/0.1/">
    <pazienti>
        <paziente>
            <codiceRegionalePaziente>0123456789</codiceRegionalePaziente>
            <codiceFiscale/>
            <nome>nomeBulk01</nome>
            <cognome>cognomeBulk01</cognome>
            <dataNascita>1989-12-31</dataNascita>
            <sesso>F</sesso>
            <nazionalita>380</nazionalita>
            <domicilioIndirizzo/>
            <domicilioCap>00019</domicilioCap>
            <domicilioComune>058104</domicilioComune>
            <domicilioProvincia>RM</domicilioProvincia>
            <residenzaIndirizzo/>
            <residenzaCap/>
            <residenzaComune/>
            <residenzaProvincia/>
            <luogoEsposizione>cinema</luogoEsposizione>
            <luogoEsposizioneComune>058047</luogoEsposizioneComune>
            <operatoreSanitario>1</operatoreSanitario>
            <casoIsolato>9</casoIsolato>
            <casoCollegato/>
            <codiceTampone>kkk12345</codiceTampone>
            <dataPrelievo>2020-03-01</dataPrelievo>
            <codLaboratorioAnalisi>999</codLaboratorioAnalisi>
            <sequenzaGenoma>9</sequenzaGenoma>
            <sequenzaInviata>0</sequenzaInviata>
            <dataInizioSintomi>2020-03-01</dataInizioSintomi>
            <codRegione>99</codRegione>
            <patologieCroniche>9</patologieCroniche>
            <tumoriAttivi/>
            <diabeteMellito/>
            <malattieCardiovascolari/>
            <hiv/>
            <malattieRespiratorieCroniche/>
            <malattieRenali/>
            <altreMalattieMetaboliche/>
            <obesitaBmi30e40/>
            <obesitaBmiOltre40/>
            <malattieEpatiche/>
            <malattieCronicheNeurologiche/>
            <altrePatologie/>
            <altrePatologieDescrizione/>
            <note>test caricamento massivo da file xml</note>
            <collocazioni>
                <collocazione>
                    <dataCollocazione>2020-03-01</dataCollocazione>
                    <collocazioneTipo>Ospedale</collocazioneTipo>
                    <ospedaleNSIS>99999999</ospedaleNSIS>
                    <ospedaleReparto>Pneumologia</ospedaleReparto>
                </collocazione>
            </collocazioni>
            <statiClinici>
                <statoClinico>
                    <tipoStatoClinico>Asintomatico</tipoStatoClinico>
                    <dataStatoClinico>2020-03-01</dataStatoClinico>
                    <terapiaInCorso>0</terapiaInCorso>
                    <terapiaDescrizione/>
                    <intubato>0</intubato>
                </statoClinico>
                <statoClinico>
                    <tipoStatoClinico>Lieve</tipoStatoClinico>
                    <dataStatoClinico>2020-03-12</dataStatoClinico>
                    <terapiaInCorso>0</terapiaInCorso>
                    <terapiaDescrizione/>
                    <intubato>0</intubato>
                </statoClinico>
                <statoClinico>
                    <tipoStatoClinico>Critico</tipoStatoClinico>
                    <dataStatoClinico>2020-03-18</dataStatoClinico>
                    <terapiaInCorso>0</terapiaInCorso>
                    <terapiaDescrizione/>
                    <intubato>1</intubato>
                </statoClinico>
            </statiClinici>
        </paziente>
        <paziente>
            <codiceRegionalePaziente>AABB1234567890</codiceRegionalePaziente>
            <codiceFiscale>AAABBB00C11D222E</codiceFiscale>
            <nome>nomeBulk02</nome>
            <cognome>cognomeBulk02</cognome>
            <dataNascita>2000-01-31</dataNascita>
            <sesso>M</sesso>
            <nazionalita>380</nazionalita>
            <domicilioIndirizzo>Via del domicilio</domicilioIndirizzo>
            <domicilioCap>00100</domicilioCap>
            <domicilioComune>058091</domicilioComune>
            <domicilioProvincia>RM</domicilioProvincia>
            <residenzaIndirizzo/>
            <residenzaCap/>
            <residenzaComune/>
            <residenzaProvincia/>
            <luogoEsposizione>centro commerciale</luogoEsposizione>
            <luogoEsposizioneComune>058091</luogoEsposizioneComune>
            <operatoreSanitario>0</operatoreSanitario>
            <casoIsolato>1</casoIsolato>
            <casoCollegato/>
            <codiceTampone>00AABB-CC</codiceTampone>
            <dataPrelievo>2020-02-29</dataPrelievo>
            <codLaboratorioAnalisi>999</codLaboratorioAnalisi>
            <sequenzaGenoma>1</sequenzaGenoma>
            <sequenzaInviata>0</sequenzaInviata>
            <dataInizioSintomi>2020-02-29</dataInizioSintomi>
            <codRegione>99</codRegione>
            <patologieCroniche>1</patologieCroniche>
            <tumoriAttivi>0</tumoriAttivi>
            <diabeteMellito>0</diabeteMellito>
            <malattieCardiovascolari>1</malattieCardiovascolari>
            <hiv>0</hiv>
            <malattieRespiratorieCroniche>1</malattieRespiratorieCroniche>
            <malattieRenali>0</malattieRenali>
            <altreMalattieMetaboliche>0</altreMalattieMetaboliche>
            <obesitaBmi30e40>0</obesitaBmi30e40>
            <obesitaBmiOltre40>0</obesitaBmiOltre40>
            <malattieEpatiche>0</malattieEpatiche>
            <malattieCronicheNeurologiche>0</malattieCronicheNeurologiche>
            <altrePatologie>1</altrePatologie>
            <altrePatologieDescrizione>descrizione altra patologia cronica</altrePatologieDescrizione>
            <note>test caricamento massivo da file xml</note>
            <collocazioni>
                <collocazione>
                    <dataCollocazione>2020-03-01</dataCollocazione>
                    <collocazioneTipo>Domicilio</collocazioneTipo>
                    <ospedaleNSIS/>
                    <ospedaleReparto/>
                </collocazione>
                <collocazione>
                    <dataCollocazione>2020-03-05</dataCollocazione>
                    <collocazioneTipo>Ospedale</collocazioneTipo>
                    <ospedaleNSIS>99999999</ospedaleNSIS>
                    <ospedaleReparto>Malattie infettive e tropicali</ospedaleReparto>
                </collocazione>
            </collocazioni>
            <statiClinici>
                <statoClinico>
                    <tipoStatoClinico>Pauci-sintomatico</tipoStatoClinico>
                    <dataStatoClinico>2020-02-29</dataStatoClinico>
                    <terapiaInCorso>0</terapiaInCorso>
                    <terapiaDescrizione/>
                    <intubato>0</intubato>
                </statoClinico>
                <statoClinico>
                    <tipoStatoClinico>Severo</tipoStatoClinico>
                    <dataStatoClinico>2020-03-17</dataStatoClinico>
                    <terapiaInCorso>1</terapiaInCorso>
                    <terapiaDescrizione>descrizione  della terapia in corso</terapiaDescrizione>
                    <intubato>0</intubato>
                </statoClinico>
            </statiClinici>
        </paziente>
        <paziente>
            <codiceRegionalePaziente>9999999</codiceRegionalePaziente>
            <codiceFiscale/>
            <nome>nomeBulk03</nome>
            <cognome>cognomeBulk03</cognome>
            <dataNascita>2000-01-31</dataNascita>
            <sesso>M</sesso>
            <nazionalita>380</nazionalita>
            <domicilioIndirizzo>Via del domicilio</domicilioIndirizzo>
            <domicilioCap>00100</domicilioCap>
            <domicilioComune>058091</domicilioComune>
            <domicilioProvincia>RM</domicilioProvincia>
            <residenzaIndirizzo/>
            <residenzaCap/>
            <residenzaComune/>
            <residenzaProvincia/>
            <luogoEsposizione>centro commerciale</luogoEsposizione>
            <luogoEsposizioneComune>058091</luogoEsposizioneComune>
            <operatoreSanitario>0</operatoreSanitario>
            <casoIsolato>1</casoIsolato>
            <casoCollegato/>
            <codiceTampone>00AABB-CC</codiceTampone>
            <dataPrelievo>2020-02-29</dataPrelievo>
            <codLaboratorioAnalisi>999</codLaboratorioAnalisi>
            <sequenzaGenoma>1</sequenzaGenoma>
            <sequenzaInviata>0</sequenzaInviata>
            <dataInizioSintomi>2020-02-29</dataInizioSintomi>
            <codRegione>99</codRegione>
            <patologieCroniche>1</patologieCroniche>
            <tumoriAttivi>0</tumoriAttivi>
            <diabeteMellito>0</diabeteMellito>
            <malattieCardiovascolari>1</malattieCardiovascolari>
            <hiv>0</hiv>
            <malattieRespiratorieCroniche>1</malattieRespiratorieCroniche>
            <malattieRenali>0</malattieRenali>
            <altreMalattieMetaboliche>0</altreMalattieMetaboliche>
            <obesitaBmi30e40>0</obesitaBmi30e40>
            <obesitaBmiOltre40>0</obesitaBmiOltre40>
            <malattieEpatiche>0</malattieEpatiche>
            <malattieCronicheNeurologiche>0</malattieCronicheNeurologiche>
            <altrePatologie>1</altrePatologie>
            <altrePatologieDescrizione>descrizione altra patologia cronica</altrePatologieDescrizione>
            <note>test caricamento massivo da file xml</note>
            <collocazioni>
            </collocazioni>
            <statiClinici>
            </statiClinici>
        </paziente>
    </pazienti>
</covid-19>

I need to split it into 3 regular table type dataset (pazienti, collocazioni, statiClinici) and I use the following T-SQL code that works:

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON
DECLARE @StartTime datetime = getdate()
    -- Insert statements for procedure here
    -- DROP temp tables
    if object_id('tempdb..#xmlPazienti') is not null DROP TABLE #xmlPazienti;
    if object_id('tempdb..#xmlCollocazioni') is not null DROP TABLE #xmlCollocazioni;
    if object_id('tempdb..#xmlStatiClinici') is not null DROP TABLE #xmlStatiClinici;

    -- pazienti --
    CREATE TABLE #xmlPazienti
    (
        [patientId] [int] NULL
        ,[codiceFiscale] [varchar](16) NULL
        ,[nome] [nvarchar](255) NULL
        ,[cognome] [nvarchar](255) NULL
        ,[dataNascita] [datetime] NULL
        ,[sesso] [char](1) NULL
        ,[nazionalita] [smallint] NULL
        ,[domicilioInd] [varchar](255) NULL
        ,[domicilioCap] [varchar](10) NULL
        ,[domicilioCom] [varchar](255) NULL
        ,[domicilioProv] [varchar](255) NULL
        ,[residenzaInd] [varchar](255) NULL
        ,[residenzaCap] [varchar](10) NULL
        ,[residenzaCom] [varchar](255) NULL
        ,[residenzaProv] [varchar](255) NULL
        ,[luogoEsp] [nvarchar](500) NULL
        ,[luogoEspCom] [varchar](255) NULL
        ,[operatoreSanitario] [tinyint] NULL
        ,[casoIsolato] [tinyint] NULL
        ,[casoCollegato] [varchar](500) NULL
        ,[CodiceTampone] [varchar](255) NULL
        ,[dataPrelievo] [date] NULL
        ,[LaboratorioAnalisiId] [smallint] NULL
        ,[sequenzaGenoma] [tinyint] NULL
        ,[sequenzaInviata] [tinyint] NULL
        ,[DataInizioSintomi] [date] NULL
        ,[codRegione] [smallint] NULL
        ,[PatologieCroniche] [tinyint] NULL
        ,[TumoriAttivi] [tinyint] NULL
        ,[DiabeteMellito] [tinyint] NULL
        ,[MalattieCardiovascolari] [tinyint] NULL
        ,[HIV] [tinyint] NULL
        ,[MalattieRespiratorieCroniche] [tinyint] NULL
        ,[MalattieRenali] [tinyint] NULL
        ,[AltreMalattieMetaboliche] [tinyint] NULL
        ,[ObesitàBMI30e40] [tinyint] NULL
        ,[ObesitàBMIoltre40] [tinyint] NULL
        ,[MalattieEpatiche] [tinyint] NULL
        ,[MalattieCronicheNeurologiche] [tinyint] NULL
        ,[AltrePatologie] [tinyint] NULL
        ,[AltrePatologieDescrizione] [nvarchar](500) NULL
        ,[Note] [nvarchar](4000) NULL
        ,[patientID_reg] [nvarchar](50) NULL
        ,[flagBaseline] [int] NULL
    )
    -- pazienti --

    -- collocazioni --
    CREATE TABLE #xmlCollocazioni
    (
    dataRicovero date NULL
    ,collocazioneId int NULL
    ,idOspedale nvarchar(255) NULL
    ,CodReparto int NULL
    ,patientID int NULL
    ,patientID_reg nvarchar(50) NULL
    ,collocazioneTipo nvarchar(50) NULL
    ,ospedaleReparto nvarchar(255) NULL
    );
    -- collocazioni --

    -- stati clinici --
    CREATE TABLE #xmlStatiClinici
    (
    patientID int NULL
    ,statoClinicoId int NULL
    ,dataStatoClinico date NULL
    ,terapiaInCorso int NULL
    ,terapia nvarchar(1000) NULL
    ,Intubato int NULL
    ,patientID_reg nvarchar(50) NULL
    ,tipoStatoClinico nvarchar(50) NULL
    );
    -- stati clinici --

    -- index on temp tables --
        CREATE INDEX ixTmp_patient_patientId ON #xmlPazienti (patientId);
        CREATE INDEX ixTmp_patient_patientID_reg ON #xmlPazienti (patientID_reg);
        CREATE INDEX ixTmp_patient_codRegione ON #xmlPazienti (codRegione) INCLUDE (patientId);
        CREATE INDEX ixTmp_patient_LaboratorioAnalisiId ON #xmlPazienti (LaboratorioAnalisiId);
        CREATE INDEX ixTmp_ricovero_patientId ON #xmlCollocazioni (patientId);
        CREATE INDEX ixTmp_ricovero_patientID_reg ON #xmlCollocazioni (patientID_reg);
        CREATE INDEX ixTmp_ricovero_collocazioneID ON #xmlCollocazioni (collocazioneID);
        CREATE INDEX ixTmp_ricovero_idOspedale ON #xmlCollocazioni (idOspedale);
        CREATE INDEX ixTmp_ricovero_codReparto ON #xmlCollocazioni (codReparto);
        CREATE INDEX ixTmp_monitoring_patientId ON #xmlStatiClinici (patientId);
        CREATE INDEX ixTmp_monitoring_patientID_reg ON #xmlStatiClinici (patientID_reg);
        CREATE INDEX ixTmp_monitoring_statoClinicoId ON #xmlStatiClinici (statoClinicoId);
    -- index on temp tables --


DECLARE @StartTimeInsertPazienti datetime = getdate()
DECLARE @fId INT = 187
DECLARE @XML AS XML
SELECT @XML = XMLData FROM XMLbulkLoad WHERE Id = @fId
;WITH XMLNAMESPACES(DEFAULT 'http://covid-19.iss.it/XMLSchema/0.1/')
    -- pazienti --
    INSERT INTO #xmlPazienti WITH (TABLOCK) ([patientID_reg],[codiceFiscale],[cognome],[nome],[dataNascita],[sesso],[nazionalita],[domicilioInd],[domicilioCap],[domicilioCom],[domicilioProv],[residenzaInd],[residenzaCap],[residenzaCom],[residenzaProv],[luogoEsp],[luogoEspCom],[operatoreSanitario],[casoIsolato],[casoCollegato],[CodiceTampone],[dataPrelievo],[LaboratorioAnalisiId],[sequenzaGenoma],[sequenzaInviata],[DataInizioSintomi],[codRegione],[PatologieCroniche],[TumoriAttivi],[DiabeteMellito],[MalattieCardiovascolari],[HIV],[MalattieRespiratorieCroniche],[MalattieRenali],[AltreMalattieMetaboliche],[ObesitàBMI30e40],[ObesitàBMIoltre40],[MalattieEpatiche],[MalattieCronicheNeurologiche],[AltrePatologie],[AltrePatologieDescrizione],[Note])
    SELECT   ISNULL(paz.value('(codiceRegionalePaziente/text())[1]', 'nvarchar(50)'),NULL) AS [patientID_reg]  
            ,ISNULL(paz.value('(codiceFiscale/text())[1]', 'varchar(16)'),NULL) AS [codiceFiscale]
            ,ISNULL(paz.value('(cognome/text())[1]', 'nvarchar(255)'),NULL) AS [cognome]
            ,ISNULL(paz.value('(nome/text())[1]', 'nvarchar(255)'),NULL) AS [nome]
            ,ISNULL(paz.value('(dataNascita/text())[1]', 'datetime'),NULL) AS [dataNascita]
            ,ISNULL(paz.value('(sesso/text())[1]', 'char(1)'),NULL) AS [sesso]
            ,ISNULL(paz.value('(nazionalita/text())[1]', 'smallint'),NULL) AS [nazionalita]
            ,ISNULL(paz.value('(domicilioIndirizzo/text())[1]', 'varchar(255)'),NULL) AS [domicilioInd]
            ,ISNULL(paz.value('(domicilioCap/text())[1]', 'varchar(10)'),NULL) AS [domicilioCap]
            ,ISNULL(paz.value('(domicilioComune/text())[1]', 'varchar(255)'),NULL) AS [domicilioCom]
            ,ISNULL(paz.value('(domicilioProvincia/text())[1]', 'varchar(255)'),NULL) AS [domicilioProv]
            ,ISNULL(paz.value('(residenzaIndirizzo/text())[1]', 'varchar(255)'),NULL) AS [residenzaInd]
            ,ISNULL(paz.value('(residenzaCap/text())[1]', 'varchar(10)'),NULL) AS [residenzaCap]
            ,ISNULL(paz.value('(residenzaComune/text())[1]', 'varchar(255)'),NULL) AS [residenzaCom]
            ,ISNULL(paz.value('(residenzaProvincia/text())[1]', 'varchar(255)'),NULL) AS [residenzaProv]
            ,ISNULL(paz.value('(luogoEsposizione/text())[1]', 'nvarchar(500)'),NULL) AS [luogoEsp]
            ,ISNULL(paz.value('(luogoEsposizioneComune/text())[1]', 'varchar(255)'),NULL) AS [luogoEspCom]
            ,ISNULL(paz.value('(operatoreSanitario/text())[1]', 'tinyint'),NULL) AS [operatoreSanitario]
            ,ISNULL(paz.value('(casoIsolato/text())[1]', 'tinyint'),NULL) AS [casoIsolato]
            ,ISNULL(paz.value('(casoCollegato/text())[1]', 'varchar(500)'),NULL) AS [casoCollegato]
            ,ISNULL(paz.value('(codiceTampone/text())[1]', 'varchar(255)'),NULL) AS [CodiceTampone]
            ,ISNULL(paz.value('(dataPrelievo/text())[1]', 'date'),NULL) AS [dataPrelievo]
            ,ISNULL(paz.value('(codLaboratorioAnalisi/text())[1]', 'smallint'),NULL) AS [LaboratorioAnalisiId]
            ,ISNULL(paz.value('(sequenzaGenoma/text())[1]', 'tinyint'),NULL) AS [sequenzaGenoma]
            ,ISNULL(paz.value('(sequenzaInviata/text())[1]', 'tinyint'),NULL) AS [sequenzaInviata]
            ,ISNULL(paz.value('(dataInizioSintomi/text())[1]', 'date'),NULL) AS [dataInizioSintomi]
            ,ISNULL(paz.value('(codRegione/text())[1]', 'smallint'),'') AS [codRegione]
            ,ISNULL(paz.value('(patologieCroniche/text())[1]', 'tinyint'),NULL) AS [patologieCroniche]
            ,ISNULL(paz.value('(tumoriAttivi/text())[1]', 'tinyint'),NULL) AS [tumoriAttivi]
            ,ISNULL(paz.value('(diabeteMellito/text())[1]', 'tinyint'),NULL) AS [diabeteMellito]
            ,ISNULL(paz.value('(malattieCardiovascolari/text())[1]', 'tinyint'),NULL) AS [malattieCardiovascolari]
            ,ISNULL(paz.value('(hiv/text())[1]', 'tinyint'),NULL) AS [hiv]
            ,ISNULL(paz.value('(malattieRespiratorieCroniche/text())[1]', 'tinyint'),NULL) AS [malattieRespiratorieCroniche]
            ,ISNULL(paz.value('(malattieRenali/text())[1]', 'tinyint'),NULL) AS [malattieRenali]
            ,ISNULL(paz.value('(altreMalattieMetaboliche/text())[1]', 'tinyint'),NULL) AS [altreMalattieMetaboliche]
            ,ISNULL(paz.value('(obesitaBmi30e40/text())[1]', 'tinyint'),NULL) AS [ObesitàBMI30e40]
            ,ISNULL(paz.value('(obesitaBmiOltre40/text())[1]', 'tinyint'),NULL) AS [ObesitàBMIoltre40]
            ,ISNULL(paz.value('(malattieEpatiche/text())[1]', 'tinyint'),NULL) AS [malattieEpatiche]
            ,ISNULL(paz.value('(malattieCronicheNeurologiche/text())[1]', 'tinyint'),NULL) AS [malattieCronicheNeurologiche]
            ,ISNULL(paz.value('(altrePatologie/text())[1]', 'tinyint'),NULL) AS [altrePatologie]
            ,ISNULL(paz.value('(altrePatologieDescrizione/text())[1]', 'nvarchar(500)'),NULL) AS [altrePatologieDescrizione]
            ,ISNULL(paz.value('(note/text())[1]', 'nvarchar(4000)'),NULL) AS [note]
    FROM  
         @XML.nodes('covid-19/pazienti/paziente') AS A(paz)
         --OPTION (OPTIMIZE FOR UNKNOWN)
         --OPTION (RECOMPILE)
    -- pazienti --
DECLARE @EndTimeInsertPazienti datetime = getdate()
SELECT DATEDIFF (SS ,@StartTimeInsertPazienti,@EndTimeInsertPazienti) AS PazientiInsertDuration

DECLARE @StartTimeInsertCollocazioni datetime = getdate()
    -- collocazioni --
    --SELECT @XML = XMLData FROM XMLbulkLoadDEV WHERE Id = @fID
    --;WITH XMLNAMESPACES(DEFAULT 'http://covid-19.iss.it/XMLSchema/0.1/')
    INSERT INTO #xmlCollocazioni WITH (TABLOCK) (patientID_reg, dataRicovero, collocazioneTipo, idOspedale, ospedaleReparto)
    SELECT  COALESCE(paz.value('(codiceRegionalePaziente/text())[1]', 'nvarchar(50)'),NULL) AS [patientID_reg]      
            ,COALESCE(coll.value('(dataCollocazione/text())[1]', 'date'),NULL) AS [dataRicovero]
            ,COALESCE(coll.value('(collocazioneTipo/text())[1]', 'nvarchar(50)'),NULL) AS [collocazioneTipo]
            ,COALESCE(coll.value('(ospedaleNSIS/text())[1]', 'nvarchar(255)'),NULL) AS [idOspedale]
            ,COALESCE(coll.value('(ospedaleReparto/text())[1]', 'nvarchar(255)'),NULL) AS [ospedaleReparto]
    FROM  
         @XML.nodes('covid-19/pazienti/paziente') AS A(paz) 
     OUTER APPLY 
         paz.nodes('collocazioni/collocazione') B(coll)
         --OPTION (OPTIMIZE FOR UNKNOWN)
         --OPTION (RECOMPILE)
    -- collocazioni --
DECLARE @EndTimeInsertCollocazioni datetime = getdate()
SELECT DATEDIFF (SS ,@StartTimeInsertCollocazioni,@EndTimeInsertCollocazioni) AS CollocazioniInsertDuration

DECLARE @StarTimeInsertStatiClinici datetime = getdate()

    -- stati clinici --
    --SELECT @XML = XMLData FROM XMLbulkLoadDEV WHERE Id = @fID
    --;WITH XMLNAMESPACES(DEFAULT 'http://covid-19.iss.it/XMLSchema/0.1/')
    INSERT INTO #xmlStatiClinici WITH (TABLOCK) (patientID_reg, tipoStatoClinico, dataStatoClinico, terapiaInCorso, terapia, intubato)
    SELECT   ISNULL(paz.value('(codiceRegionalePaziente/text())[1]', 'nvarchar(50)'),NULL) AS [patientID_reg]      
            ,ISNULL(sc.value('(tipoStatoClinico/text())[1]', 'nvarchar(50)'),NULL) AS [tipoStatoClinico]
            ,ISNULL(sc.value('(dataStatoClinico/text())[1]', 'date'),NULL) AS [dataStatoClinico]
            ,ISNULL(sc.value('(terapiaInCorso/text())[1]', 'int'),0) AS [terapiaInCorso]
            ,ISNULL(sc.value('(terapiaDescrizione/text())[1]', 'nvarchar(1000)'),NULL) AS [terapia]
            ,ISNULL(sc.value('(intubato/text())[1]', 'int'),NULL) AS [intubato]
    FROM  
         @XML.nodes('covid-19/pazienti/paziente') AS A(paz) 
     OUTER APPLY 
         paz.nodes('statiClinici/statoClinico') C(sc)
        --OPTION (OPTIMIZE FOR UNKNOWN)
        --OPTION (RECOMPILE)
    -- stati clinici --
DECLARE @EndTimeInsertStatiClinici datetime = getdate()
SELECT DATEDIFF (SS ,@StarTimeInsertStatiClinici,@EndTimeInsertStatiClinici) AS StatiCliniciInsertDuration
    --
    --select * from XMLbulkLoad

DECLARE @EndTimeInsert datetime = getdate()
SELECT DATEDIFF (SS ,@StartTimeInsertPazienti,@EndTimeInsertStatiClinici) AS TotalInsertDuration

    -- DROP temp tables
    if object_id('tempdb..#xmlPazienti') is not null DROP TABLE #xmlPazienti;
    if object_id('tempdb..#xmlCollocazioni') is not null DROP TABLE #xmlCollocazioni;
    if object_id('tempdb..#xmlStatiClinici') is not null DROP TABLE #xmlStatiClinici;

The problem is that when there are a few hundred (or fewer) elements in the XML, the query performs just fine. However, when there are 25,000 elements, it takes 50 seconds to finish returning the rows in SSMS and I could have 50-100,000 elements.

Is there a more efficient way to transform the XML document into the tabular dataset (in SQL)?


回答1:


One general hint might be: create your indexes as the final step. Especially with a lot of rows this will slow down any data manipulation (in this case your insert statements);

As pointed in my comment you might walk the staging route.

First I create a mockup table to simulate your issue

DECLARE @tbl TABLE(YourXML XML);
INSERT INTO @tbl VALUES
(N'<covid-19 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://covid-19.iss.it/XMLSchema/0.1/">
    <pazienti>
        <paziente>
            <codiceRegionalePaziente>0123456789</codiceRegionalePaziente>
            <codiceFiscale/>
            <nome>nomeBulk01</nome>
            <cognome>cognomeBulk01</cognome>
            <!-- shortened -->
            <collocazioni>
                <collocazione>
                    <dataCollocazione>2020-03-01</dataCollocazione>
                    <!-- shortened -->
                </collocazione>
            </collocazioni>
            <statiClinici>
                <statoClinico>
                    <tipoStatoClinico>Asintomatico</tipoStatoClinico>
                    <!-- shortened -->
                </statoClinico>
                <statoClinico>
                    <tipoStatoClinico>Lieve</tipoStatoClinico>
                    <!-- shortened -->
                </statoClinico>
                <statoClinico>
                    <tipoStatoClinico>Critico</tipoStatoClinico>
                    <!-- shortened -->
                </statoClinico>
            </statiClinici>
        </paziente>
        <paziente>
            <codiceRegionalePaziente>AABB1234567890</codiceRegionalePaziente>
            <codiceFiscale>AAABBB00C11D222E</codiceFiscale>
            <nome>nomeBulk02</nome>
            <cognome>cognomeBulk02</cognome>
            <!-- shortened -->
            <collocazioni>
                <collocazione>
                    <dataCollocazione>2020-03-01</dataCollocazione>
                    <!-- shortened -->
                </collocazione>
                <collocazione>
                    <dataCollocazione>2020-03-05</dataCollocazione>
                    <!-- shortened -->
                </collocazione>
            </collocazioni>
            <statiClinici>
                <statoClinico>
                    <tipoStatoClinico>Pauci-sintomatico</tipoStatoClinico>
                    <!-- shortened -->
                </statoClinico>
                <statoClinico>
                    <tipoStatoClinico>Severo</tipoStatoClinico>
                    <!-- shortened -->
                </statoClinico>
            </statiClinici>
        </paziente>
        <paziente>
            <codiceRegionalePaziente>9999999</codiceRegionalePaziente>
            <codiceFiscale/>
            <nome>nomeBulk03</nome>
            <cognome>cognomeBulk03</cognome>
            <!-- shortened -->
            <collocazioni>
            </collocazioni>
            <statiClinici>
            </statiClinici>
        </paziente>
    </pazienti>
</covid-19>');

--The query

WITH XMLNAMESPACES(DEFAULT 'http://covid-19.iss.it/XMLSchema/0.1/')
SELECT   paz.value('(codiceRegionalePaziente/text())[1]', 'nvarchar(50)') AS [patientID_reg]  
        ,paz.value('(codiceFiscale/text())[1]', 'varchar(16)') AS [codiceFiscale]
        ,paz.value('(cognome/text())[1]', 'nvarchar(255)') AS [cognome]
        ,paz.value('(nome/text())[1]', 'nvarchar(255)') AS [nome]
        --shortended

        ,coll.value('(dataCollocazione/text())[1]', 'date') AS [dataRicovero]
        --shortended

        ,sc.value('(tipoStatoClinico/text())[1]', 'nvarchar(50)') AS [tipoStatoClinico]
        --shortended

 --Define a target staging table simply automatically
 INTO #StagingTable

 FROM @tbl t
 CROSS APPLY t.YourXML.nodes('covid-19/pazienti/paziente') AS A(paz)
 OUTER APPLY 
     paz.nodes('collocazioni/collocazione') B(coll)

 OUTER APPLY 
     paz.nodes('statiClinici/statoClinico') C(sc);

--now all your data is shifted into one big table

SELECT * FROM #StagingTable

--use something like this to get the patient data

SELECT patientID_reg,codiceFiscale,cognome,nome /*all patient related columns*/ 
FROM #StagingTable 
GROUP BY patientID_reg,codiceFiscale,cognome,nome /*all patient related columns*/;

--This will your related data.

SELECT patientID_reg,dataRicovero /*all collocazioni related data*/ 
FROM #StagingTable 
GROUP BY patientID_reg,dataRicovero /*all collocazioni related data*/

SELECT patientID_reg,tipoStatoClinico /*all stati clinici related data*/ 
FROM #StagingTable 
GROUP BY patientID_reg,tipoStatoClinico /*all stati clinici related data*/

You can perform any cleaning, or check of business logic, against the staged data. It might be a good idea to check for existing patientID_reg values in your target table...
Then shift the cleaned data into your final target tables.

UPDATE

Attention: If your XML might carry the same patientID_reg value for different elements you will have to add a ROW_NUMBER()

In this case you might use something along this:

WITH XMLNAMESPACES(DEFAULT 'http://covid-19.iss.it/XMLSchema/0.1/')
,ReadPatientDataWithRowNumber AS
(
    SELECT   ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS PatientNumber
            ,paz.value('(codiceRegionalePaziente/text())[1]', 'nvarchar(50)') AS [patientID_reg]  
            ,paz.value('(codiceFiscale/text())[1]', 'varchar(16)') AS [codiceFiscale]
            ,paz.value('(cognome/text())[1]', 'nvarchar(255)') AS [cognome]
            ,paz.value('(nome/text())[1]', 'nvarchar(255)') AS [nome]
            --shortended
            ,paz.query('collocazioni/collocazione') AS CollocazioneXml
            ,paz.query('statiClinici/statoClinico') AS StatoClinicoXml
     FROM @tbl t
     CROSS APPLY t.YourXML.nodes('covid-19/pazienti/paziente') AS A(paz)
)
,AddTheRest AS
(
    SELECT pd.*

    ,coll.value('(dataCollocazione/text())[1]', 'date') AS [dataRicovero]
    --shortended

    ,sc.value('(tipoStatoClinico/text())[1]', 'nvarchar(50)') AS [tipoStatoClinico]
    --shortended

    FROM ReadPatientDataWithRowNumber pd
     OUTER APPLY 
         pd.CollocazioneXml.nodes('collocazioni/collocazione') B(coll)

     OUTER APPLY 
         pd.StatoClinicoXml.nodes('statiClinici/statoClinico') C(sc)
)   
SELECT * 
INTO #StagingTable
FROM AddTheRest


SELECT PatientNumber,patientID_reg,codiceFiscale,cognome,nome /*all patient related columns*/ 
FROM #StagingTable 
GROUP BY PatientNumber,patientID_reg,codiceFiscale,cognome,nome /*all patient related columns*/;

SELECT PatientNumber,patientID_reg,dataRicovero /*all collocazioni related data*/ 
FROM #StagingTable 
GROUP BY PatientNumber,patientID_reg,dataRicovero /*all collocazioni related data*/

SELECT PatientNumber,patientID_reg,tipoStatoClinico /*all stati clinici related data*/ 
FROM #StagingTable 
GROUP BY PatientNumber,patientID_reg,tipoStatoClinico /*all stati clinici related data*/

UPDATE 2

Please check this:

WITH XMLNAMESPACES(DEFAULT 'http://covid-19.iss.it/XMLSchema/0.1/')
,ReadPatientDataWithRowNumber AS
(
    SELECT   ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS PatientNumber
            ,paz.value('(codiceRegionalePaziente/text())[1]', 'nvarchar(50)') AS [patientID_reg]  
            ,paz.value('(codiceFiscale/text())[1]', 'varchar(16)') AS [codiceFiscale]
            ,paz.value('(cognome/text())[1]', 'nvarchar(255)') AS [cognome]
            ,paz.value('(nome/text())[1]', 'nvarchar(255)') AS [nome]
            --shortended
            ,paz.query('collocazioni/collocazione') AS CollocazioneXml
            ,paz.query('statiClinici/statoClinico') AS StatoClinicoXml
     FROM @tbl t
     CROSS APPLY t.YourXML.nodes('covid-19/pazienti/paziente') AS A(paz)
)
SELECT * 
INTO #StagingTable
FROM ReadPatientDataWithRowNumber

SELECT * FROM #StagingTable;


来源:https://stackoverflow.com/questions/61608313/efficient-way-to-transform-xml-document-into-tabular-dataset-in-sql-because-cros

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!