Risposte:
PostgreSQL ha una guida su come popolare inizialmente un database e suggeriscono di usare il comando COPY per il caricamento di file di massa. La guida ha alcuni altri buoni consigli su come velocizzare il processo, come la rimozione di indici e chiavi esterne prima di caricare i dati (e aggiungerli successivamente).
Esiste un'alternativa all'utilizzo di COPY, che è la sintassi dei valori multirow che supporta Postgres. Dalla documentazione :
INSERT INTO films (code, title, did, date_prod, kind) VALUES
('B6717', 'Tampopo', 110, '1985-02-10', 'Comedy'),
('HG120', 'The Dinner Game', 140, DEFAULT, 'Comedy');
Il codice sopra inserisce due righe, ma è possibile estenderlo arbitrariamente, fino a quando non si raggiunge il numero massimo di token di istruzione preparati (potrebbe essere $ 999, ma non ne sono sicuro al 100%). A volte non si può usare COPIA, e questo è un degno sostituto per quelle situazioni.
Un modo per velocizzare le cose è eseguire esplicitamente più inserti o copie all'interno di una transazione (diciamo 1000). Il comportamento predefinito di Postgres è il commit dopo ogni istruzione, quindi raggruppando i commit, è possibile evitare un certo sovraccarico. Come dice la guida nella risposta di Daniel, potrebbe essere necessario disabilitare l'autocommit perché funzioni. Nota anche il commento in basso che suggerisce di aumentare le dimensioni dei wal_buffer a 16 MB.
UNNEST
la funzione con le matrici può essere utilizzata insieme alla sintassi multirow VALUES. Penso che questo metodo sia più lento dell'uso COPY
ma è utile per me nel lavoro con psycopg e python (python list
passato a cursor.execute
diventa pg ARRAY
):
INSERT INTO tablename (fieldname1, fieldname2, fieldname3)
VALUES (
UNNEST(ARRAY[1, 2, 3]),
UNNEST(ARRAY[100, 200, 300]),
UNNEST(ARRAY['a', 'b', 'c'])
);
senza VALUES
utilizzare la sottoselezione con ulteriore controllo dell'esistenza:
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
);
la stessa sintassi per gli aggiornamenti collettivi:
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;
Puoi usare COPY table TO ... WITH BINARY
" leggermente più veloce dei formati di testo e CSV ". Fallo solo se hai milioni di righe da inserire e se sei a tuo agio con i dati binari.
Ecco una ricetta di esempio in Python, usando psycopg2 con input binario .
Dipende principalmente dall'attività (altra) nel database. Operazioni come questa congelano efficacemente l'intero database per altre sessioni. Un'altra considerazione è il modello di dati e la presenza di vincoli, trigger, ecc.
Il mio primo approccio è sempre: creare una tabella (temp) con una struttura simile alla tabella di destinazione (creare una tabella tmp AS selezionare * dalla destinazione dove 1 = 0) e iniziare leggendo il file nella tabella temporanea. Quindi controllo cosa è possibile verificare: duplicati, chiavi già esistenti nella destinazione, ecc.
Quindi faccio semplicemente un "do insert in target select * from tmp" o simile.
Se fallisce o impiega troppo tempo, lo interrompo e considero altri metodi (eliminazione temporanea di indici / vincoli, ecc.)
Ho implementato un caricatore di dati Postgresq molto veloce con metodi nativi libpq. Prova il mio pacchetto https://www.nuget.org/packages/NpgsqlBulkCopy/
Ho appena riscontrato questo problema e consiglierei csvsql ( versioni ) per le importazioni in blocco in Postgres. Per eseguire un inserimento in blocco, devi semplicemente createdb
e poi utilizzare csvsql
, che si collega al tuo database e crea tabelle individuali per un'intera cartella di CSV.
$ createdb test
$ csvsql --db postgresql:///test --insert examples/*.csv
Il termine "dati in blocco" si riferisce a "molti dati", quindi è naturale utilizzare dati grezzi originali , senza la necessità di trasformarli in SQL. I file di dati grezzi tipici per "l'inserimento di massa" sono CSV e JSON formati .
Nelle applicazioni ETL e nei processi di acquisizione, è necessario modificare i dati prima di inserirli. La tabella temporanea consuma (molto) spazio su disco e non è il modo più veloce per farlo. Il wrapper di dati esterni (FDW) PostgreSQL è la scelta migliore.
Esempio CSV . Supponiamo che tablename (x, y, z)
su SQL e un file CSV come
fieldname1,fieldname2,fieldname3
etc,etc,etc
... million lines ...
È possibile utilizzare il classico SQL COPY
per caricare ( come i dati originali) tmp_tablename
, inserire i dati filtrati in tablename
... Ma, per evitare il consumo del disco, il meglio è ingerire direttamente da
INSERT INTO tablename (x, y, z)
SELECT f1(fieldname1), f2(fieldname2), f3(fieldname3) -- the transforms
FROM tmp_tablename_fdw
-- WHERE condictions
;
Devi preparare il database per FDW e invece statico tmp_tablename_fdw
puoi usare una funzione che lo genera :
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');
Esempio JSON . Un set di due file myRawData1.json
e Ranger_Policies2.json
può essere ingerito da:
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
;
dove la funzione jsonb_read_files () legge tutti i file di una cartella, definita da una maschera:
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;
Il metodo più frequente di "ingestione di file" (principalmente in Big Data) è preservare il file originale in formato gzip e trasferirlo con algoritmo di streaming , tutto ciò che può essere eseguito velocemente e senza consumo di disco in pipe unix:
gunzip remote_or_local_file.csv.gz | convert_to_sql | psql
Quindi l'ideale (futuro) è un'opzione server per il formato .csv.gz
.