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 Beispielcsv_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.