在 SQL 中将 XML 文档转换为表格数据集的有效方法,因为随着 xml 的增长,交叉应用 xml 查询的性能呈指数级下降

Posted

技术标签:

【中文标题】在 SQL 中将 XML 文档转换为表格数据集的有效方法,因为随着 xml 的增长,交叉应用 xml 查询的性能呈指数级下降【英文标题】:efficient way to transform XML document into tabular dataset in SQL because cross apply xml query performs exponentially worse as xml grows 【发布时间】:2020-05-05 07:54:37 【问题描述】:

我有一个大尺寸的 XML 文档 (50.000-100,000),需要在 Azure SQL 上进行解析,如下所示:

<?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>

我需要将其拆分为 3 个常规表类型数据集(pazienti、collocazioni、statiClinici),并使用以下有效的 T-SQL 代码:

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;

问题是当 XML 中有几百个(或更少)元素时,查询执行得很好。但是,当有 25,000 个元素时,完成返回 SSMS 中的行需要 50 秒,而我可能有 50-100,000 个元素。

有没有更有效的方法将 XML 文档转换为表格数据集(在 SQL 中)?

【问题讨论】:

为了迂腐,我怀疑它是二次的而不是指数的。它有所作为,因为 (a) 二次方并没有那么糟糕,并且 (b) 知道差异有助于找出原因。遗憾的是,虽然我不太了解您使用的数据库技术,但无法查明原因。 (1) 您可以尝试通过直接从 XMLbulkLoad 表访问 XML 来分​​解 XML,而不使用 @XML 变量。 (2) 不清楚为什么要对每个数据元素使用 ISNULL() 函数。根据源 XML,它们将有自己的 NULL 值.. @YitzhakKhabinsky 我尝试直接从 XMLbulkLoad 表中分解 XML:没有改进。然后我在表上添加了一个 xml 索引,查询只需要 12 秒但是.. 表上的批量加载从 4 秒到 35 秒!!!所以整个过程更糟糕。有什么建议吗? 使用 XML 最昂贵的操作通常是初始解析。列 XMLData FROM XMLbulkLoad 是原生 XML 类型的? it might help to read the whole lot in one staging table and fill your target tables from there with SELECT ... GROUP BY` 语句。 @Shnugo 是的,XMLData 列本身是 XM 类型的。当您说“在一个临时表中阅读全部内容并从那里用 SELECT ... GROUP BY` 语句填充您的目标表”时,我不明白:您的意思是什么?你能提供一个简短的例子吗? 【参考方案1】:

一般提示可能是:创建索引作为最后一步。尤其是对于很多行,这会减慢任何数据操作(在这种情况下是您的插入语句);

正如我在评论中指出的那样,您可能会走临时路线

首先我创建一个模型表来模拟您的问题

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>');

--查询

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);

--现在你的所有数据都转移到一张大表中

SELECT * FROM #StagingTable

--使用类似的东西来获取患者数据

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

--这将是您的相关数据。

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*/

您可以针对暂存数据执行任何清理或业务逻辑检查。检查目标表中现有的 patientID_reg 值可能是个好主意... 然后将清理后的数据转移到您的最终目标表中。

更新

注意:如果您的 XML 可能为不同的元素携带相同的 patientID_reg 值,则必须添加 ROW_NUMBER()

在这种情况下,您可能会使用以下内容:

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*/

更新 2

请检查:

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;

【讨论】:

谢谢,现在我理解了你的方法,这也是我第一次尝试使用一个大的临时表。我不认为它可能会提高响应时间,因为插入语句是相同的,但现在有一个交叉连接,所以插入更大..暂时忘记相关表,只是想耐心表:我绝对需要删除插入时间 @mtallon 好吧,这对你来说可能很清楚,但对我来说不是......只查询一次 XML 应该更快......而且我看不到 CROSS JOIN......你可以使用我的 UPDATE 部分中的代码没有 CTE AddTheRest。这会将每个&lt;paziente&gt; 插入一行到#StagingTable,而相关的辅助数据保留为 XML 片段。从那里开始应该很容易...... 你有 nr 的笛卡尔积。患者 x nr。 collocazioni x nr. staticlinici 因此,例如,如果我添加另一个元素 collocazione ,它将导致 stagingtable 中的 11 条记录而不是 3 条。无论如何,我尝试了您的 UPDATE 中的代码,我在#StagingTable 中插入了 3 名患者,3 条记录为空 dataRicovero 和 3 条记录为空TipoStatoClinico...抱歉,如果我不明白... @malton 检查更新 2 部分。至少对于给定的示例,这显示了值... 抱歉我的回复晚了。我尝试您的解决方案更新 2:如果我在表上添加 xml 索引,您的查询只需 8 秒(我的查询需要 12 秒);但最初的批量插入需要更多时间与索引(35 秒 - 而只有 4 没有索引)。如果我在没有索引的表上尝试你的 UPDATE 2,我的查询需要 35 秒,而你的查询在 6 分钟后仍在运行......

以上是关于在 SQL 中将 XML 文档转换为表格数据集的有效方法,因为随着 xml 的增长,交叉应用 xml 查询的性能呈指数级下降的主要内容,如果未能解决你的问题,请参考以下文章

在 SQL Server 中将数据转换为 XML 时出错

如何在 SQL 中将带有其他字段的 XML 解析为多行?

在 JavaScript 中将字符串转换为 XML 文档

在sql server中将行/表记录转换为json文档

在 php 中将 XML 文档转换为数组时,有没有办法将其转换回来并将其保存为属性为元素的 XML 文件?

在 Groovy 中将字符串 XML 片段转换为文档节点