web-dev-qa-db-fra.com

PHP - Importer un fichier CSV dans la base de données mysql en utilisant LOAD DATA INFILE

J'ai un fichier .csv comme ça

Date,Name,Call Type,Number,Duration,Address,PostalCode,City,State,Country,Latitude,Longitude
"Sep-18-2013 01:53:45 PM","Unknown","outgoing call",'123456',"0 Secs","null","null","null","null","null",0.0,0.0,,,
"Sep-18-2013 01:54:14 PM","Unknown","outgoing call",'1234567890',"0 Secs","null","null","null","null","null",0.0,0.0,,,
"Sep-18-2013 01:54:37 PM","Unknown","outgoing call",'14772580369',"1 Secs","null","null","null","null","null",0.0,0.0,,,

et j'utilise le code suivant pour insérer les données dans la base de données 

$sql = "LOAD DATA INFILE `detection.csv`
              INTO TABLE `calldetections`
              FIELDS TERMINATED BY '".@mysql_escape_string(",").
             "` OPTIONALLY ENCLOSED BY `".@mysql_escape_string("\"").
             "` OPTIONALLY ENCLOSED BY `".@mysql_escape_string("\'").
             "` ESCAPED BY `".@mysql_escape_string("\\").
              "` LINES TERMINATED BY `".",,,\\r\\n".
             "`IGNORE 1 LINES `"

             ."(`date`,`name`,`type`,`number`,`duration`,`addr`,`pin`,`city`,`state`,`country`,`lat`,`log`)";
      $res = @mysql_query($con,$sql); 

mais rien n'est inséré; où est l'erreur?

14
chimbu

Si vous exécutiez echo($sql); avant de l'exécuter, la syntaxe de votre requête serait incorrecte pour les raisons suivantes:

  1. Le nom de fichier doit être placé entre guillemets et non entre guillemets car c'est un littéral de chaîne et non un identifiant.

  2. Il n'est absolument pas nécessaire d'appeler mysql_escape_string() pour spécifier un délimiteur dans les clauses FIELDS TERMINATED BY et ENCLOSED BY et ESCAPED BY.

  3. Vous abusez des backticks. En fait, dans votre cas, comme aucun mot réservé n’est utilisé, vous les abandonnez tous. Ils ne font qu'ajouter de l'encombrement.

  4. À la fin de la toute première ligne de votre fichier CSV, vous devez disposer de,,, car vous les utilisez dans le cadre d'un séparateur de ligne. Si vous ne le faites pas, vous passerez non seulement à la première ligne, mais également à la seconde qui contient des données.

  5. Vous ne pouvez pas utiliser la clause ENCLOSED BY plus d'une fois. Vous devez traiter le champ Number d'une manière différente.

  6. En regardant vos exemples de lignes IMHO, vous n'avez pas besoin de ESCAPED BY. Mais si vous sentez que vous en avez besoin, utilisez-le comme ceci ESCAPED BY '\\'.

Cela étant dit, une déclaration syntaxiquement correcte pourrait ressembler à ceci

LOAD DATA INFILE 'detection.csv'
INTO TABLE calldetections
FIELDS TERMINATED BY ','
OPTIONALLY ENCLOSED BY '"' 
LINES TERMINATED BY ',,,\r\n'
IGNORE 1 LINES 
(date, name, type, number, duration, addr, pin, city, state, country, lat, log)

Maintenant, à mon humble avis, vous devez transformer plusieurs champs pendant que vous les chargez:

  1. si date dans votre table est du type de données datetime, il doit être transformé, sinon vous obtiendrez une erreur. 

    Valeur de date/heure incorrecte: 'Sep-18-2013 01:53:45 PM' pour la colonne 'date' à la ligne 

  2. vous devez traiter avec des qoutes simples autour des valeurs dans le champ Number

  3. vous voudrez probablement changer la chaîne "null" en littéral NULL pour les colonnes addr, pin, city, state, country

  4. si la durée est toujours en secondes, vous pouvez extraire une valeur entière de secondes et la stocker ainsi dans votre table pour pouvoir facilement agréger les valeurs de durée ultérieurement.

Cela étant dit, une version utile de la déclaration devrait ressembler à ceci:

LOAD DATA INFILE 'detection.csv'
INTO TABLE calldetections
FIELDS TERMINATED BY ','
OPTIONALLY ENCLOSED BY '"' 
LINES TERMINATED BY ',,,\r\n'
IGNORE 1 LINES 
(@date, name, type, @number, @duration, @addr, @pin, @city, @state, @country, lat, log)
SET date = STR_TO_DATE(@date, '%b-%d-%Y %h:%i:%s %p'),
    number = TRIM(BOTH '\'' FROM @number),
    duration = 1 * TRIM(TRAILING 'Secs' FROM @duration),
    addr = NULLIF(@addr, 'null'),
    pin  = NULLIF(@pin, 'null'),
    city = NULLIF(@city, 'null'),
    state = NULLIF(@state, 'null'),
    country = NULLIF(@country, 'null') 

Voici le résultat de l'exécution de la requête sur ma machine

 mysql> LOAD DATA INFILE '/tmp/detection.csv'
 -> INTO TABLEdetections d'appels 
 -> ZONES TERMINÉES PAR ',' 
 -> OPTIONNELLEMENT FERMÉ PAR '' '' 
 -> LIGNES TERMINÉES PAR ',\n' 
 -> IGNORER 1 LIGNES 
 -. (@Date, nom, type, @numéro, @durée, @addr, @pin, @city, @state, @country, lat, log) 
 -> SET date = STR_TO_DATE (@date, '% b-% d-% Y% h:% i:% s% p '), 
 -> nombre = TRIM (BOTH'\'' FROM @number), 
 -> duration = 1 * TRIM (TRAILING 'Secs' FROM @duration), 
 -> addr = NULLIF (@addr, 'null'), 
 -> pin = NULLIF (@pin, 'null'), 
 -> ville = NULLIF (@city, 'null'), 
 -> état = NULLIF (@state, 'null'), 
 -> country = NULLIF (@country, 'null'); 
 Requête OK, 3 lignes affectées (0,00 s) .__ Enregistrements: 3 Supprimés: 0 Skipped: 0 Avertissements: 0 

 Mysql> select * from calldetections; 
 + --------------------- + ----- ---- + --------------- + ------------- + ---------- + ---- - + ------ + ------ + ------- + --------- + ------ + ------ +
 | date | nom | type | numéro | durée | addr | épingle | ville | état | pays | lat | journal | 
 + --------------------- + --------- + ------------- - + ------------- + ---------- + ------ + ------ + ------ + - ------ + --------- + ------ + ------ + 
 | 2013-09-18 13:53:45 | Inconnu | appel sortant | 123456 | 0 | NULL | NULL | NULL | NULL | NULL | 0,0 | 0,0 | 
 | 2013-09-18 13:54:14 | Inconnu | appel sortant | 1234567890 | 0 | NULL | NULL | NULL | NULL | NULL | 0,0 | 0,0 | 
 | 2013-09-18 13:54:37 | Inconnu | appel sortant | 14772580369 | 1 | NULL | NULL | NULL | NULL | NULL | 0,0 | 0.0 | 
 + --------------------- + --------- + ------------ --- + ------------- + ---------- + ------ + ------ + ------ + ------- + --------- + ------ + ------ + 
 3 rangées dans le jeu (0.00 sec) 

Et enfin en php assigner une chaîne de requête à la variable $sql devrait ressembler à ceci

$sql = "LOAD DATA INFILE 'detection.csv'
        INTO TABLE calldetections
        FIELDS TERMINATED BY ','
        OPTIONALLY ENCLOSED BY '\"' 
        LINES TERMINATED BY ',,,\\r\\n'
        IGNORE 1 LINES 
        (@date, name, type, @number, @duration, @addr, @pin, @city, @state, @country, lat, log)
        SET date = STR_TO_DATE(@date, '%b-%d-%Y %h:%i:%s %p'),
            number = TRIM(BOTH '\'' FROM @number),
            duration = 1 * TRIM(TRAILING 'Secs' FROM @duration),
            addr = NULLIF(@addr, 'null'),
            pin  = NULLIF(@pin, 'null'),
            city = NULLIF(@city, 'null'),
            state = NULLIF(@state, 'null'),
            country = NULLIF(@country, 'null') ";
40
peterm

Insérer en bloc plus de 7000000 enregistrements en 1 minute dans la base de données (requête ultra-rapide avec calcul)

    mysqli_query($cons, '
    LOAD DATA LOCAL INFILE "'.$file.'"
    INTO TABLE tablename
    FIELDS TERMINATED by \',\'
    LINES TERMINATED BY \'\n\'
    IGNORE 1 LINES
    (isbn10,isbn13,price,discount,free_stock,report,report_date)
     SET RRP = IF(discount = 0.00,price-price * 45/100,IF(discount = 0.01,price,IF(discount != 0.00,price-price * discount/100,@RRP))),
         RRP_nl = RRP * 1.44 + 8,
         RRP_bl = RRP * 1.44 + 8,
         ID = NULL
    ')or die(mysqli_error());
    $affected = (int) (mysqli_affected_rows($cons))-1; 
    $log->lwrite('Inventory.CSV to database:'. $affected.' record inserted successfully.');

RRP et RRP_nl et RRP_bl ne sont pas dans csv mais nous calculons cela et après l'insérons.

0
krunal panchal