Importer des données XML dans une base de données MySQL

1 message   •   Page 1 sur 1
Avatar de l’utilisateur
Administrateur PHPfrance
Administrateur PHPfrance | 7234 Messages

07 oct. 2008, 09:03

Bonjour à tous,

J'ai eu à bosser récemment sur un script d'importation de données issues d'un fichier XML dans une base de données MySQL, voici le résultat de mes recherches pour effectuer cette opération.

Il existe 3 possibilités pour faire ceci:
1) Via XSLT
2) Via PHP
3) Via MySQL 6


Avant de décrire les différents scénarios, petit comparatif avantage/inconvénient:
1) Via XSLT
Avantages : Permet de faire des traitements assez élaboré quand on connait bien XSLT; peut être utilisé avec LOAD DATA INFILE pour une importation ultra rapide
Inconvénient : Assez difficile à prendre en main si vous n'avez jamais utilisé XSLT auparavant.
2) Via PHP
Avantages : Contrôle total (=flexibilité) des données à importer; simplicité d'accès aux données grâce à SimpleXML; Langage PHP uniquement
Inconvénients : Très lourd à développer si le XML contient de nombreux champs de données; Inadapté pour des gros fichiers XML (car temps de traitement assez élevé).
3) Via MySQL 6
Avantages : Quelques lignes suffisent; Importation ultra-rapide
Inconvénient : Disponible qu'à partir de la version 6 de MySQL


-------------------------------------------
1) Via XSLT :
Pour ceux qui ne connaissent pas XSLT, il s'agit en fait d'une feuille de style qui permet de mettre en forme un fichier XML.
Image [1]

Dans notre cas d'utilisation, on va en fait utiliser XSLT pour transformer le XML afin d'obtenir un fichier contenant des requêtes SQL.
Exemple [2] :
test.xml

Code : Tout sélectionner

<?xml version="1.0" ?> <?xml-stylesheet href="test.xsl" type="text/xsl"?> <articles> <article> <nom>tutu</nom> <id>0</id> </article> <article> <nom>titi</nom> <id>1</id> </article> </articles>
test.xsl

Code : Tout sélectionner

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="text"/> <xsl:template match="/"> <xsl:apply-templates select="//article"/> </xsl:template> <xsl:template match="article"> <xsl:text>insert into article (id, lib) values(</xsl:text> <xsl:value-of select="./id"/> <xsl:text>,'</xsl:text> <xsl:value-of select="./nom"/> <xsl:text>');&#xA;</xsl:text> </xsl:template> </xsl:stylesheet>
Ce qui permet d'obtenir:

Code : Tout sélectionner

insert into article (id, lib) values('0','tutu'); insert into article (id, lib) values('1','titi');
Pour effectuer l'importation, voici la ligne de commande:

Code : Tout sélectionner

mysql < xalan -in test.xml -xsl test.xsl
A noter qu'il est également envisageable avec XSLT de produire un fichier CSV qu'on pourra ensuite importer avec la fonction MySQL LOAD DATA INFILE et ainsi faire une importation en quelques secondes.
:arrow: Voir le tutoriel PHPfrance à ce sujet.


-------------------------------------------
2) via PHP :
Là il s'agit plus classiquement de lire le fichier XML pour ensuite faire les requêtes MySQL qui vont bien pour importer ligne par ligne ce que l'on veut.
C'est souvent la première solution qui vient en tête des développeurs PHP et c'est la plus simple quand on a des fichiers XML très petits, malheureusement cela devient très rapidement fastidieux si il y a beaucoup de champs dans le XML à mapper.
Par ailleurs, si le fichier XML source devient un peu conséquent, le serveur ne va pas tenir la charge (car avec cette méthode on effectue les traitements et requêtes SQL ligne par ligne...)
http://fr.php.net/simplexml

Exemple :
test.xml

Code : Tout sélectionner

<?xml version="1.0" ?> <articles> <article> <nom>tutu</nom> <id>0</id> </article> <article> <nom>titi</nom> <id>1</id> </article> </articles>
test.php
<?php
$articles_xml = simplexml_load_file('test.xml');
foreach ($articles_xml ->article as $article) {
    print "Nom de l'article: {$article->nom} <br />\n";
    print "Id de l'article: {$article->id} <hr />\n";
    mysql_query("INSERT INTO articles SET nom='".($article->nom)."' AND id='".($article->id)."'") or die("Erreur MySQL : ".mysql_error());
} 
?>

-------------------------------------------
3) via MySQL 6 :
Voici donc la dernière et 3ème méthode pour importer du XML.
:!: IMPORTANT :!:
Cette solution utilise la fonction MySQL LOAD XML INFILE qui n'est disponible qu'à partir de MySQL 6.
Si vous essayez cette méthode sans avoir MySQL 6 cela ne marchera évidemment pas.
A noter, MySQL 6 est au moment de rédiger ce tuto (octobre 2008), encore en version alpha, par conséquent il n'est pas conseillé de l'utiliser en production.
Si vous utilisez WampServer, MySQL6 est disponible en module "clé-en-main": http://www.wampserver.com/addons_dev.php

:arrow: Alors concrètement, à quoi sert la fonction LOAD XML INFILE?
En fait il s'agit simplement de prendre un fichier XML et d'insérer son contenu directement dans une table MySQL.

Code : Tout sélectionner

<row> <column1>value1</column1> <column2>value2</column2> <column3>value3</column3> </row>
A noter que l'importation se fait de façon intelligente, c'est à dire que si je n'ai pas dans ma table MySQL cible de champ "column2", MySQL va importer quand même la ligne en renseignant column1, column3 et en ignorant les valeurs XML qui n'ont pas de champ MySQL équivalent dans la table.

En terme de performance, et afin de vous donner une idée de la vitesse d'exécution, j'importe un programme TV complet pour 5 chaines avec une profondeur d'une semaine -soit environ 2000 émissions- avec toutes les métadonnées associées (titre, sous-titre, chaine, date de diff, heure de début, heure de fin, résumé court, résumé long, auteur, réalisateur, scénariste, acteurs, présence d'un sous-titrage, langue, pays de prod, etc...) en 18 secondes sur une machine bas de gamme (Athlon XP 2600 à 1.9GHz et 1Go de Ram)

Cela en fait une solution très très simple à mettre en oeuvre (juste quelques lignes de code) et ultra-performante pour importer en masse des données depuis un fichier XML.

En utilisant cette solution, j'ai trouvé un bug (normal vu que je travaille sur une version alpha) qui bloque l'importation lorsqu'on a des monobalises sans valeur.
Vous trouverez dans mon code ci-dessous les deux lignes de PHP qui permettent de retirer ces monobalises afin que l'importation se passe sans problème.

Exemple :
test.xml

Code : Tout sélectionner

<?xml version="1.0" ?> <articles> <article> <nom>tutu</nom> <id>0</id> <prix/> </article> <article> <nom>titi</nom> <id>1</id> <prix>12.21</prix> </article> </articles>
test.php
<?php
// On récupère le contenu du fichier XML source
$xml_source=file_get_contents("test.xml");

// On retire les balises vides
$motif="`<[a-zA-Z0-9_]*/>`i";
$xml_source_clean=preg_replace($motif, "", $xml_source);

// On met le code XML nettoyé des balises vides dans un fichier temporaire
file_put_contents("temp.xml", $xml_source_clean);

// On importe tout ça dans la BDD
mysql_query("LOAD XML INFILE 'temp.xml' INTO TABLE articles ROWS identified by '<article>'") or die("Erreur MySQL : ".mysql_error());

// On supprime le fichier temporaire
unlink("temp.xml");
[1] Source: Article XSLT de Wikipedia
[2] Exemple issu du forum de developpez.net
Quand tout le reste a échoué, lisez le mode d'emploi...

1 message   •   Page 1 sur 1