Antworten:
PostgreSQL enthält eine Anleitung, wie eine Datenbank zunächst am besten gefüllt werden kann, und es wird empfohlen, den Befehl COPY zum Laden von Zeilen in großen Mengen zu verwenden . Das Handbuch enthält einige weitere gute Tipps zur Beschleunigung des Vorgangs, z. B. das Entfernen von Indizes und Fremdschlüsseln vor dem Laden der Daten (und das anschließende Hinzufügen dieser Daten).
Es gibt eine Alternative zur Verwendung von COPY, der von Postgres unterstützten Syntax für Mehrzeilenwerte. Aus der Dokumentation :
INSERT INTO films (code, title, did, date_prod, kind) VALUES
('B6717', 'Tampopo', 110, '1985-02-10', 'Comedy'),
('HG120', 'The Dinner Game', 140, DEFAULT, 'Comedy');
Der obige Code fügt zwei Zeilen ein, aber Sie können ihn beliebig erweitern, bis Sie die maximale Anzahl vorbereiteter Anweisungstoken erreicht haben (es könnte 999 US-Dollar sein, aber da bin ich mir nicht 100% sicher). Manchmal kann man COPY nicht verwenden, und dies ist ein würdiger Ersatz für diese Situationen.
Eine Möglichkeit, die Dinge zu beschleunigen, besteht darin, explizit mehrere Einfügungen oder Kopien innerhalb einer Transaktion durchzuführen (z. B. 1000). Das Standardverhalten von Postgres besteht darin, nach jeder Anweisung ein Commit durchzuführen. Wenn Sie also die Commits stapeln, können Sie einen gewissen Overhead vermeiden. Wie der Leitfaden in Daniels Antwort sagt, müssen Sie möglicherweise die automatische Festschreibung deaktivieren, damit dies funktioniert. Beachten Sie auch den Kommentar unten, der darauf hinweist, dass die Größe der wal_buffers auf 16 MB erhöht werden kann.
UNNEST
Funktion mit Arrays kann zusammen mit der Multirow-VALUES-Syntax verwendet werden. Ich denke , ich , dass diese Methode ist langsamer als die Verwendung , COPY
aber es ist nützlich für mich in der Arbeit mit psycopg und Python (Python list
weitergegeben cursor.execute
wird pg ARRAY
):
INSERT INTO tablename (fieldname1, fieldname2, fieldname3)
VALUES (
UNNEST(ARRAY[1, 2, 3]),
UNNEST(ARRAY[100, 200, 300]),
UNNEST(ARRAY['a', 'b', 'c'])
);
ohne VALUES
Unterauswahl mit zusätzlicher Existenzprüfung:
INSERT INTO tablename (fieldname1, fieldname2, fieldname3)
SELECT * FROM (
SELECT UNNEST(ARRAY[1, 2, 3]),
UNNEST(ARRAY[100, 200, 300]),
UNNEST(ARRAY['a', 'b', 'c'])
) AS temptable
WHERE NOT EXISTS (
SELECT 1 FROM tablename tt
WHERE tt.fieldname1=temptable.fieldname1
);
die gleiche Syntax für Massenaktualisierungen:
UPDATE tablename
SET fieldname1=temptable.data
FROM (
SELECT UNNEST(ARRAY[1,2]) AS id,
UNNEST(ARRAY['a', 'b']) AS data
) AS temptable
WHERE tablename.id=temptable.id;
Sie können verwenden, COPY table TO ... WITH BINARY
was " etwas schneller als die Text- und CSV-Formate " ist. Tun Sie dies nur, wenn Sie Millionen von Zeilen einfügen müssen und wenn Sie mit Binärdaten vertraut sind.
Hier ist ein Beispielrezept in Python, das psycopg2 mit binärer Eingabe verwendet .
Dies hängt hauptsächlich von der (anderen) Aktivität in der Datenbank ab. Mit solchen Vorgängen wird die gesamte Datenbank für andere Sitzungen effektiv eingefroren. Eine weitere Überlegung ist das Datenmodell und das Vorhandensein von Einschränkungen, Auslösern usw.
Mein erster Ansatz ist immer: Erstellen Sie eine (temporäre) Tabelle mit einer Struktur ähnlich der Zieltabelle (erstellen Sie eine Tabelle tmp AS select * from target mit 1 = 0) und lesen Sie zunächst die Datei in die temporäre Tabelle. Dann überprüfe ich, was überprüft werden kann: Duplikate, Schlüssel, die bereits im Ziel vorhanden sind usw.
Dann mache ich einfach ein "In Ziel einfügen select * from tmp" oder ähnliches.
Wenn dies fehlschlägt oder zu lange dauert, brich ich es ab und erwäge andere Methoden (vorübergehendes Löschen von Indizes / Einschränkungen usw.)
Ich habe einen sehr schnellen Postgresq Data Loader mit nativen libpq-Methoden implementiert. Probieren Sie mein Paket https://www.nuget.org/packages/NpgsqlBulkCopy/ aus.
Ich bin gerade auf dieses Problem gestoßen und würde csvsql ( Releases ) für Massenimporte nach Postgres empfehlen . Um eine Masseneinfügung durchzuführen, verwenden Sie einfach createdb
und dann eine csvsql
, die eine Verbindung zu Ihrer Datenbank herstellt und einzelne Tabellen für einen gesamten Ordner mit CSVs erstellt.
$ createdb test
$ csvsql --db postgresql:///test --insert examples/*.csv
Der Begriff "Massendaten" bezieht sich auf "viele Daten". Daher ist es selbstverständlich, Original-Rohdaten zu verwenden , ohne diese in SQL umwandeln zu müssen. Typische Rohdatendateien für "Masseneinfügungen" sind CSV- und JSON- Formate.
In ETL- Anwendungen und Aufnahmeprozessen müssen wir die Daten vor dem Einfügen ändern. Temporäre Tabellen belegen (viel) Speicherplatz, und dies ist nicht der schnellere Weg. Der PostgreSQL Foreign Wrapper (FDW) ist die beste Wahl.
CSV-Beispiel . Angenommen, die tablename (x, y, z)
On-SQL- und eine CSV-Datei mögen
fieldname1,fieldname2,fieldname3
etc,etc,etc
... million lines ...
Sie können das klassische SQL COPY
zum Laden ( wie auch für Originaldaten) verwenden tmp_tablename
und gefilterte Daten in tablename
... einfügen. Um jedoch den Festplattenverbrauch zu vermeiden, sollten Sie am besten direkt von aufnehmen
INSERT INTO tablename (x, y, z)
SELECT f1(fieldname1), f2(fieldname2), f3(fieldname3) -- the transforms
FROM tmp_tablename_fdw
-- WHERE condictions
;
Sie müssen die Datenbank für FDW vorbereiten, und stattdessen tmp_tablename_fdw
können Sie statisch eine Funktion verwenden, die sie generiert :
CREATE EXTENSION file_fdw;
CREATE SERVER import FOREIGN DATA WRAPPER file_fdw;
CREATE FOREIGN TABLE tmp_tablename_fdw(
...
) SERVER import OPTIONS ( filename '/tmp/pg_io/file.csv', format 'csv');
JSON-Beispiel . Ein Satz von zwei Dateien, myRawData1.json
und Ranger_Policies2.json
kann durch eingenommen werden:
INSERT INTO tablename (fname, metadata, content)
SELECT fname, meta, j -- do any data transformation here
FROM jsonb_read_files('myRawData%.json')
-- WHERE any_condiction_here
;
Dabei liest die Funktion jsonb_read_files () alle Dateien eines Ordners, die durch eine Maske definiert sind:
CREATE or replace FUNCTION jsonb_read_files(
p_flike text, p_fpath text DEFAULT '/tmp/pg_io/'
) RETURNS TABLE (fid int, fname text, fmeta jsonb, j jsonb) AS $f$
WITH t AS (
SELECT (row_number() OVER ())::int id,
f as fname,
p_fpath ||'/'|| f as f
FROM pg_ls_dir(p_fpath) t(f)
WHERE f like p_flike
) SELECT id, fname,
to_jsonb( pg_stat_file(f) ) || jsonb_build_object('fpath',p_fpath),
pg_read_file(f)::jsonb
FROM t
$f$ LANGUAGE SQL IMMUTABLE;
Die häufigste Methode zur "Dateiaufnahme" (hauptsächlich in Big Data) besteht darin, die Originaldatei im gzip-Format beizubehalten und mit zu übertragen Streaming-Algorithmus zu alles, was in Unix-Pipes schnell und ohne Disc-Verbrauch ausgeführt werden kann:
gunzip remote_or_local_file.csv.gz | convert_to_sql | psql
Ideal (Zukunft) ist also eine Serveroption für das Format .csv.gz
.