tutorials.de Buch-Aktion 02/2012
  • [MySQL] Dateieinlesung

    Ich habe in letzter Zeit immer öfter die Aufgabe gehabt unterschiedlichste .CSV-Dateien in eine MySQL-Datenbank zu überführen. Da viele Unternehmen dieses Format immer noch als Austauschformat bereit halten.

    Dabei meinen es die Unternehmen meist nur "gut" und wandeln es von sich aus ins gewohnte deutsche Format um, damit die Sachbearbeiter schneller damit zurecht kommen.

    Daher möchte ich hier einfach mal eine einfache Möglichkeit zeigen, den Inport zu realisieren.

    Dafür nehmen wir uns ein Beispiel, im folgenden sei der Aufbau der CSV-Datei (;-getrennt) beschrieben:

    Code :
    1
    2
    3
    
    KundenNr    ArtikelNr1   ArtikelNr2 Anzahl  Preis   Variante    Datum       Adresse     PLZ     Land               
    001         Firma-5678   9854543241 3       4,50    2           21.12.2011  StraßeA 12  01234   DE
    002         Firma-234    9854424241 2       11,97   5           22.12.2011  StraßeB 4   12345   DE

    Sowas kennen sicher viele. Hier haben wir folgende Probleme:
    - die Kopfzeile wollen wir nicht importieren, unser Aufbau in der MySQL Datenbank entspricht ja dieser.
    - die Spalte ArtikelNr2 (z.B. interne ArtNr. vom Unternehmenspartner), sowie Variante interessiert uns nicht und wollen wir nicht mit importieren.
    - unsere Haupt-Artikelnummer ist z.B. 5stellig und wird vorne mit Nullen aufgefüllt. Zudem gehört unser Firmennamen nicht zu unserer Artikelnummer. (Andere Unternehmen machen dies oft um die Nummern zu unterscheiden)
    - der Preis ist im deutschen Format angegeben, doch um Fehler zu vermeiden speichern wir Preise als Integer, also in Cent ab.
    - die PLZ muss auch 5stellig sein. (Gerade wenn hier der Sachbearbeiter die CSV-Datei mit Excel öffnet, etwas ändert und speichert sind führende Nullen weg.)
    - das deutsche Datumsformat bringt uns in der Datenbank leider sehr wenig.

    MySQL bietet eine sehr einfache und elegante Lösung, solche Dateien zu importieren mit der Anweisung:
    Code sql:
    1
    2
    3
    
    LOAD DATA INFILE '{$file}' INTO TABLE kundentabelle 
                FIELDS TERMINATED BY ';' ENCLOSED BY ' '
                LINES TERMINATED BY '\n'
    Hierbei ist $file (schreibweise aus PHP) unserer Datei, welche importiert werden soll.
    kundentabelle beschreibt unsere Zieltabelle in der Datenbank.

    Hierbei habe ich öfter mal das Problem gehabt, dass das Leerzeichen bei ENCLOSED BY ' ' nicht anerkannt wurde, ich weiß bis heute nicht warum.
    Solltet ihr auch dieses Problem aus PHP heraus haben, löst es wie folgt:
    PHP-Code:
    $query_str sprintf("LOAD DATA INFILE '{$file}' INTO TABLE kundentabelle  
                FIELDS TERMINATED BY ';' ENCLOSED BY '%c'
                LINES TERMINATED BY '\n' "
    34); 
    Nun müssen wir unsere angegebenen Probleme lösen!
    Die Kopfzeile (1 Zeile) zu ignorieren ist sehr einfach, dazu fügen wir folgende SQL-Zeile hinzu:
    Code sql:
    1
    
    IGNORE 1 LINES

    Im folgenden zählen wir alle Spalten aus der Tabelle kundentabelle auf, die aus der CSV-Datei zu ignorierenden Spalten bezeichnen wir mit @dummy
    Code sql:
    1
    
    (KundenNr, ArtikelNr1, @dummy, Anzahl, Preis, @dummy, Datum, Adresse, PLZ, Land)
    Ich bin hierbei der Übersichtlichkeitshalber davon ausgegangen, das unsere Spalten aus der CSV-Datei deren aus der kundentabelle entsprechen.
    @dummy ist eine Variable, der kein Wert zugewiesen wird, so wird diese Spalte einfach ignoriert.

    Damit unsere Artikelnummer korrekt importiert wird beschreiben wir das Format der Spalte wie folgt:
    Code sql:
    1
    2
    3
    
    SET ArtikelNr1 =    IF(LOCATE('-',ArtikelNr1)>0,
                           LPAD(SUBSTRING(ArtikelNr1,1,LOCATE('-',ArtikelNr1)-1),5,'0'),
                           LPAD(ArtikelNr1,5,'0'))
    Wir schneiden also unseren Firmennamen (inkl. dem Bindestrich) ab und füllen unsere Artikelnummer von links mit Nullen auf.

    Mit dem Preis verfahren wir ähnlich, auch hier legen wir das Format der Spalte fest:
    Code sql:
    1
    
    SET Preis = REPLACE(Preis, ',', '.')*100
    Wir wandeln das Komma in einen Punkt um und multiplizieren mit 100, dadurch haben wir den Centbetrag.

    Das Problem der Postleitzahl ist ja bereits im Problem der Artikelnummer enthalten gewesen:
    Code sql:
    1
    
    SET PLZ = LPAD(PLZ,5,'0')

    Nun kommen wir zu einem schwierigerem Problem, dem Datum. Dazu müssen wir uns noch einmal die Auflistung unserer Spalten ansehen, wir wissen durch @dummy das es erlaubt ist dort Variablen zu definieren, doch wo werden diese Variablen beschrieben?
    Eine Möglichkeit ist hier im "SET-Bereich". Also benennen wir die Spalte Datum in @Datum um, dann sieht diese SQL-Zeile wie folgt aus:
    Code sql:
    1
    
    (KundenNr, ArtikelNr1, @dummy, Anzahl, Preis, @dummy, @Datum, Adresse, PLZ, Land)
    Nun ist es uns möglich diese Spalte zu beschreiben.
    Code sql:
    1
    
    SET Datum = STR_TO_DATE(@Datum, GET_FORMAT(DATE,'EUR'))
    Hier tauchen zwei sehr schöne MySQL-Methoden auf:
    STR_TO_DATE wandelt ein Datum nach Angabe des Formats ins "Datenbank-Format" um.
    GET_FORMAT beschreibt den Aufbau des klassischen europäischen Datumsaufbaus.

    Aber warum wurde hier nun der Umweg über eine Variable gewählt, in den vorigen Beispielen haben wir dies nicht benötigt. Das ist ganz einfach zu begründen.
    Verwenden wir keine Variablen, wird der Kontext erst importiert und anschließend angepasst. Beim Datum hätten wir dann den Effekt, das etwas Falsches in der Datenbank steht, was wir nun auch nicht mehr korrigieren brauchen.

    Daher empfehle ich allgemein mit Variablen zu operieren.

    Zur Information, mehrere SET-Befehle werden in einem SET-Block geschrieben und mit Komma getrennt.

    Insgesamt sieht der Befehl nun so aus:
    Code sql:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    
    LOAD DATA INFILE '{$file}' INTO TABLE kundentabelle 
                FIELDS TERMINATED BY ';' ENCLOSED BY ' '
                LINES TERMINATED BY '\n'
                IGNORE 1 LINES
                (KundenNr, @ArtikelNr1, @dummy, Anzahl, @Preis, @dummy, @Datum, Adresse, @PLZ, Land)
            
                SET ArtikelNr1 = IF(LOCATE('-',@ArtikelNr1)>0,
                                   LPAD(SUBSTRING(@ArtikelNr1,1,LOCATE('-',@ArtikelNr1)-1),5,'0'),
                                   LPAD(@ArtikelNr1,5,'0')),
                    Preis = REPLACE(@Preis, ',', '.')*100,
                    PLZ = LPAD(@PLZ,5,'0'),
                    Datum = STR_TO_DATE(@Datum, GET_FORMAT(DATE,'EUR'))


    Ich hoffe ich konnte einen kleinen Einblick gewähren und habe die typischten Beispiele beleuchten können.

    Viel Spaß
    Movera, enne, guenter024 und 2 weitere bedanken sich. 


    Kommentare Kommentar schreiben

    Klicke hier, um dich anzumelden

    Welche Farbe hat eine reife Zitrone?