19.122012

CSV-Dateien in Datenbanken importieren

Es kommt häufiger vor, dass man Daten, die man als kommaseparierte
Datei (.csv) vorliegen hat – oder einfach in dieses Format
konvertieren kann – in ein RDBMS importieren muss.

Die naheliegende Lösung, die Datei programmatisch einzulesen und in
mehr oder weniger optimierten SQL-Statements (X Zeilen auf einmal in
bulk inserts, … ) zum Server zu schleifen verliert gegen die
Mechanismen, die Datenbanken in der Regel für diesen Fall
bereithalten.

PostgreSQL

PostgreSQL stellt für diesen Fall den COPY-Befehl zur Verfügung. Die
Syntax aus dem Handbuch:

COPY tablename [ ( column [, ...] ) ]
    FROM { 'filename' | STDIN }
    [ [ WITH ]
          [ BINARY ]
          [ OIDS ]
          [ DELIMITER [ AS ] 'delimiter' ]
          [ NULL [ AS ] 'null string' ]
          [ CSV [ HEADER ]
                [ QUOTE [ AS ] 'quote' ]
                [ ESCAPE [ AS ] 'escape' ]
                [ FORCE NOT NULL column [, ...] ]

Der COPY Befehl unter Angabe eines Dateinamens steht nur dem
Superuser zur Verfügung, das Werkzeug psql stellt jedoch den
Operator copy zur Verfügung, mit dem auch Normalsterbliche Nutzer
Tabellen füllen können.

Im folgenden Beispiel legen wir auf einem entfernten Server in der
Datenbank test eine Tabelle an und füllen sie mit einer lokal
vorliegenden Datei.

Die Beispieldatei:

id, name
1, "test 1"
2, "test 2"
3, "test 3"

Der Dialog mit der Datenbank:

rechner:directory $ psql -h HOST -p PORT -U user test
Password for user user:
psql (9.1.6, server 8.3.15)
test=> CREATE TABLE csv_test (
test(> id integer NOT NULL PRIMARY KEY,
test(> name text NOT NULL);
NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "csv_test_pkey" for table "csv_test"
CREATE TABLE
test=> select * from csv_test;
 id | name
----+------
(0 rows)

test=> copy csv_test from '/home/USER/scratch/csv_test.csv' CSV HEADER;
test=> select * from csv_test  ;
 id |  name
----+---------
  1 |  test 1
  2 |  test 2
  3 |  test 3
(3 rows)

MySQL

Das Äquivalent zu PostgreSQL's COPY von MySQL ist LOAD DATA INFILE.

Auch hier der Auszug aus Handbuch:

LOAD DATA [LOW_PRIORITY | CONCURRENT] [LOCAL] INFILE 'file_name'
    [REPLACE | IGNORE]
    INTO TABLE tbl_name
    [CHARACTER SET charset_name]
    [{FIELDS | COLUMNS}
        [TERMINATED BY 'string']
        [[OPTIONALLY] ENCLOSED BY 'char']
        [ESCAPED BY 'char']
    ]
    [LINES
        [STARTING BY 'string']
        [TERMINATED BY 'string']
    ]
    [IGNORE number LINES]
    [(col_name_or_user_var,...)]
    [SET col_name = expr,...]

Auch hier zeigen wir das vorgehen an dem einfachen Beispiel
csv_test.

Der Dialog mit der Datenbank:

rechner:directory $ mysql  -h HOST -u USER -p test
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or g.
Your MySQL connection id is 1703

Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or 'h' for help. Type 'c' to clear the current input statement.

mysql> CREATE TABLE csv_test (
     > id integer AUTO_INCREMENT PRIMARY KEY,
     > name text );
Query OK, 0 rows affected (0.03 sec)

mysql> select * from csv_test;
Empty set (0.00 sec)

mysql> LOAD DATA LOCAL INFILE '/home/USER/scratch/csv_test.csv' INTO TABLE csv_test IGNORE 1 LINES;
Query OK, 3 rows affected, 6 warnings (0.02 sec)
Records: 3  Deleted: 0  Skipped: 0  Warnings: 6

mysql> select * from csv_test;
+----+------+
| id | name |
+----+------+
|  1 | NULL |
|  2 | NULL |
|  3 | NULL |
+----+------+
3 rows in set (0.00 sec)

mysql>

Es ist zu beachten, dass sowohl der Client als auch der Server mit
--local-infile=1 gestartet werden müssen; sonst bekommt man die
nicht ganz eindeutige Fehlermeldung

ERROR 1148 (42000): The used command is not allowed with this MySQL version

Bemerkung

Sicher ist das keine Raketenwissenschaft :-D, ich denke aber, dass
diese Information mit einem tatsächlichen Beispiel für einige
nützlich sein wird – ich habe zumindest nicht sofort eine solche
Übersicht finden können.