| CSV Datei in eine MySQL Tabelle importieren |
CSV Datei in eine MySQL Tabelle importierenHin und wieder steht man vor der Aufgabe eine CSV Datei in eine Datenbank übertragen zu müssen. Dies kann man zwar auf verschiedene Arten angehen, allerdings muss man abwägen, welche Methode am geeignetsten ist. Während es bei kleinen Dateien, mit nur wenigen Datensätzen (weniger als 1000) relativ beliebig ist, spielt die Performance und die allgemeine Serverlast, sowie die PHP Speicherlimitierung bei zunehmendem Volumen eine große Rolle. Ich möchte mal einen Blick auf die beiden typischen Methoden werfen, wie es sicher die meisten mit PHP lösen würden und dann möchte ich noch eine Methode vorstellen, die in, nennen wir es mal Amateurkreisen, wenig bekannt sein dürfte. Gehen wir mal von der Situation aus, dass wir eine stark vereinfachte CSV Datei haben, in der Datensätze mit Name und Beruf stehen. Diese soll in eine bestehende MySQL Tabelle eingefügt werden, die die Struktur hat: id, name, beruf. Eine Datenbankverbindung besteht bereits und wir sind nun an dem Punkt, an dem wir die CSV verarbeiten und in die DB bringen. Hier mal unsere Beispiel CSV Datei: Andreas;Webentwickler Michael;Schreiner Matthias;KFZ-Mechaniker Yvonne;Einzelhandelskauffrau Peter;Architekt Sabrina;Journalistin Nichts spektakuläres, nur einige Zeilen Text. Jede Zeile Text entspricht einem Datensatz. Die Felder sind mit einem Semikolon separiert. Schauen wir uns mal an, welche Methoden es gibt die Daten in die Datenbank zu befördern. 1. Die schlechteste Methode // Jeden Datensatz einzeln in die DB schreiben $benutzer_liste = file($csv_datei); foreach ($benutzer_liste as $benutzer) { list($name, $beruf) = explode(';', $benutzer); $db->query("INSERT INTO `{$tabelle}` (`name`, `beruf`) VALUES ('{$name}', '{$beruf}')"); } Zunächst wird mit der file-Funktion die CSV als Array eingelesen. In der anschließenden foreach-Schleife lesen wird Datensatz für Datensatz aus, zerlegen die Zeilen am Separator, wodurch wir die getrennten Werte Name und Beruf erhalten. Diese schicken wir dann direkt mittels query() sofort an die Datenbank, die den neuen Datensatz dann in die Tabelle schreibt. Wir schreiben also jeden Datensatz einzeln in die Datenbank. In unserer Beispiel CSV Datei befinden sich 6 Datensätze, also kontaktieren wir auch "nur" 6 mal die Datenbank. Mit dieser Methode würden wir bei einer CSV mit 10.000 Datensätzen auch "nur" 10.000 mal unsere Datenbank matern. Diese Methode verschwendet massiv Zeit und Ressourcen, deswegen ist sie ein absolutes no-go! 2. Die Notlösung // Datensätze zusammenfassen $benutzer_liste = file($csv_datei); $value_array = array(); foreach ($benutzer_liste as $benutzer) { list($name, $beruf) = explode(';', $benutzer); $value_array[] = "('{$name}', '{$beruf}')"; } $values = join(',', $value_array); $db->query("INSERT INTO `{$tabelle}` (`name`, `beruf`) VALUES {$values}"); Wir starten zunächst wie im vorherigen Beispiel, indem wir die Datei als Array einlesen und in einer Schleife durchlaufen. Bei dieser Methode schicken wir allerdings nichts an die Datenbank, sondern bilden Wertepaare, die wir dann en bloc an die Datenbank schicken. Dies geschieht in dieser Zeile: $value_array[] = "('{$name}', '{$beruf}')"; Im Array legen wir also Zeichenketten ab, die z.B. so aussehen: ('Andreas','Webentwickler') INSERT INTO `{$tabelle}` (`name`, `beruf`) VALUES ('Andreas','Webentwickler'),('Michael','Schreiner'),('Matthias','KFZ-Mechaniker') ... usw.Anschließend wird diese Anweisung nur ein Mal an die DB geschickt und alle Datensätze werden hinzugefügt. Wir kontaktieren also nicht so häufig die Datenbank, verrichten dafür aber mehr PHP-Arbeit. Wie sich das auf die Performance auswirkt wird weiter unten gezeigt. Kommen wir zur eingangs erwähnten Methode, die weniger bekannt ist. 3. Die optimale Methode // Datenbankanweisung $sql = "LOAD DATA LOCAL INFILE '{$csv_datei}' INTO TABLE `{$tabelle}` FIELDS TERMINATED BY ';' OPTIONALLY ENCLOSED BY '\"' LINES TERMINATED BY '\n' (`name`, `beruf`)"; // Query senden $db->query($sql); Wie man sieht senden wir nur eine mehr oder weniger einfache Anweisung an die Datenbank. Wir wursteln da nicht mit PHP herum, jagen keine Arrays durch Schleifen, oder ähnliches. LOAD DATA LOCAL INFILE '{$csv_datei}'Der Pfad zu der Datei muss ein absoluter Serverpfad sein. Es reicht also nicht aus nur den Dateiname zu übergeben, selbst wenn diese im gleichen Verzeichnis wie das PHP Script liegt. Dann sagen wir MySQL, wohin die geladene Datei eingefügt werden soll: INTO TABLE `{$tabelle}`Jetzt folgen Anweisungen an MySQL die der DB mitteilen, wie die Daten in der CSV vorliegen: FIELDS TERMINATED BY ';' OPTIONALLY ENCLOSED BY '\"' LINES TERMINATED BY '\n' Die erste Zeile bestimmt, dass die Werte in unserer CSV durch Semikola getrennt sind (Andreas;Webentwickler). Die Zweite Zeile sagt, dass unsere Werte auch durch Double-Quotes eingeschlossen sein dürfen. Dadurch zerbricht die Struktur nicht, falls ein Separator im Wert vorkommen würde, z.B.: ("Andreas;phpBuddy";Webentwickler). Ohne die Double-Quotes würde MySQL denken, dass Andreas der Name ist und phpBuddy der Beruf. Mit Webentwickler könnte es nichts anfangen und da man auch keine 3 Werte in 2 Spalten einfügen kann, käme es zu einem Fehler. Bleibt noch die letzte Zeile, die eigentlich selbsterklärend sein sollte. (`name`, `beruf`) Das sind schlicht die Felder in der Tabelle, in die die Werte aus der CSV eingefügt werden sollen. Das ist vergleichbar mit: INSERT INTO `tabelle` (`name`, `beruf`) VALUES (…) Auf den ersten Blick sieht die Anweisung etwas heftig aus, erklärt sich dann aber sehr schnell von allein. Fragt sich jetzt aber, was das effektiv bringt im Vergleich zu den anderen Methoden. Der Methodenvergleich
Wie zu erkennen ist stinkt die 1. Methode total ab. Sie dauert sehr lange und mit 6 MB RAM Verbrauch ist sie auch nicht gerade sparsam. Bedenkt man, dass man auf Shared Hosting Server, also normaler Mietwebspace, häufig nur 8 MB RAM für PHP zur Verfügung hat, wird das schon richtig eng, wenn noch andere Aktivitäten auf dem Server stattfinden. Ich hoffe dieser Tipp war dem Ein oder Anderen eine Hilfe. Im Download befinden sich alle 3 Methoden in einer Datei und dazu auch eine kleine CSV Testdatei. Viele Grüße CSV in MySQL importieren (1 KB) |