Barracuda Blog


Web, Tecnologia e Mare
0

Caricare file XML di grandi dimensioni con Mysql

Nell'ambito di un progetto e-commerce, mi sono trovato a dover caricare dei file XML che contenevano informazioni sui prodotti (es: nomi, scheda descrittiva, categorie, ecc.). Questi file dovevano essere caricati su tabelle di un database Mysql versione 5.7 a determinati intervalli.

Ho utilizzato il comando nativo di Mysql:

LOAD XML LOCAL INFILE nome_file INTO TABLE nome_tabella ROWS IDENTIFIED BY '<RECORD>';

Purtroppo ho riscontrato l'impossibilità di caricare file più grandi di 10-15 MB in tempi accettabili, in quanto il numero di righe caricate dopo qualche minuto crollava da qualche centinaia a poche unità al secondo. Ho provato a ottimizzare i parametri del database, senza ottenere alcun miglioramento. Sembrerebbe un problema di Mysql.

Dal momento che, invece, il caricamento dei file CSV è molto veloce, ho pensato di utilizzare una trasformazione XSLT per convertire il file da XML a CSV. Questo è un esempio di file XML da convertire:

<?xml version="1.0"?>
<ALLRECORDSDATASET>
  <RECORD>
    <FDI_0001>000367058</FDI_0001>
    <FDI_0004>Nome Prodotto</FDI_0004>
    <FDI_0040>3733</FDI_0040>
    <FDI_0008>D</FDI_0008>
    <FDI_0012>D</FDI_0012>
    <FDI_0329>A06AB06</FDI_0329>
    <FDI_0245>S</FDI_0245>
    <FDI_0250>2003-06-18</FDI_0250>
    <FDI_9007>8.500</FDI_9007>
    <FDI_9144>2009-09-01</FDI_9144>
    <FDI_9145>7.700</FDI_9145>
    <FDI_9146>2009-03-10</FDI_9146>
    <FDI_0248>10</FDI_0248>
  </RECORD>
  ....
  ....
</ALLRECORDSDATASET>

Il file XSL che ho usato è il seguente:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="text"/>
    <xsl:variable name="delimiter" select="','"/>
    <xsl:strip-space elements="*"/>
    <xsl:key name="field" match="/*/*/*" use="local-name()"/>

    <!-- variable containing the first occurrence of each field -->
    <xsl:variable name="allFields"
         select="/*/*/*[generate-id()=generate-id(key('field', local-name())[1])]" />

    <xsl:template match="/">
        <xsl:for-each select="$allFields">
            <xsl:value-of select="local-name()" />
            <xsl:if test="position() &lt; last()">
                <xsl:value-of select="$delimiter" />
            </xsl:if>
        </xsl:for-each>
        <xsl:text>&#10;</xsl:text>
        <xsl:apply-templates select="*/*" />
    </xsl:template>

    <xsl:template match="*">
        <xsl:variable name="this" select="." />
        <xsl:for-each select="$allFields">
            <xsl:variable name="x" select="$this/*[local-name() = local-name(current())]" />
                        <xsl:value-of select="concat('$', $x, '$')"/>
            <xsl:if test="position() &lt; last()">
                <xsl:value-of select="$delimiter" />
            </xsl:if>
        </xsl:for-each>
        <xsl:text>&#10;</xsl:text>
    </xsl:template>
</xsl:stylesheet>

Questo XSL trasforma il file XML in un file CSV avente come colonne i nome dei tag all'interno dei tag <RECORD> e come delimitatore dei campi il carattere $.

Per effettuare la trasformazione ho usato SAXON (la versione .NET), che tra tutti i processori xslt è risultato quello che consuma meno memoria. Scordatevi di usare sistemi operativi a 32-bit e un quantitativo di ram inferiore a 8 GB. Durante una trasformazione di un file XML di 500 MB ho rilevato un consumo di memoria di oltre 3 GB.

Una volta ottenuto il file CSV, caricarlo sarà molto semplice e impiegherà qualche secondo con il comando:

LOAD DATA LOCAL INFILE 'file.csv' INTO TABLE nome_tabella FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '$' LINES TERMINATED BY '\n' IGNORE 1 LINES (campo1, campo2, ...) SET ID = null;

Categories: Web Development

Tags: xml, mysql, xslt, csv

Be the first to comment

Post a comment