Respuestas:
PostgreSQL tiene una guía sobre cómo poblar mejor una base de datos inicialmente, y sugieren utilizar el comando COPIAR para filas de carga masiva. La guía tiene algunos otros buenos consejos sobre cómo acelerar el proceso, como eliminar índices y claves foráneas antes de cargar los datos (y agregarlos nuevamente).
Existe una alternativa al uso de COPY, que es la sintaxis de valores de varias filas que admite Postgres. De la documentación :
INSERT INTO films (code, title, did, date_prod, kind) VALUES
('B6717', 'Tampopo', 110, '1985-02-10', 'Comedy'),
('HG120', 'The Dinner Game', 140, DEFAULT, 'Comedy');
El código anterior inserta dos filas, pero puede extenderlo arbitrariamente, hasta alcanzar el número máximo de tokens de declaración preparados (puede ser $ 999, pero no estoy 100% seguro de eso). A veces no se puede usar COPY, y este es un reemplazo digno para esas situaciones.
Una forma de acelerar las cosas es realizar explícitamente múltiples inserciones o copias dentro de una transacción (digamos 1000). El comportamiento predeterminado de Postgres es confirmar después de cada declaración, por lo que al agrupar las confirmaciones, puede evitar algunos gastos generales. Como dice la guía en la respuesta de Daniel, es posible que deba desactivar la confirmación automática para que esto funcione. También tenga en cuenta que el comentario en la parte inferior que sugiere aumentar el tamaño de wal_buffers a 16 MB también puede ayudar.
UNNEST
La función con matrices se puede utilizar junto con la sintaxis VALORES de varias filas. Creo que este método es más lento que el uso, COPY
pero es útil para mí en el trabajo con psycopg y python (python list
pasado a se cursor.execute
convierte en pg ARRAY
):
INSERT INTO tablename (fieldname1, fieldname2, fieldname3)
VALUES (
UNNEST(ARRAY[1, 2, 3]),
UNNEST(ARRAY[100, 200, 300]),
UNNEST(ARRAY['a', 'b', 'c'])
);
sin VALUES
usar subseleccionar con verificación de existencia adicional:
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 misma sintaxis para las actualizaciones masivas:
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;
Puede usar el COPY table TO ... WITH BINARY
cual es " algo más rápido que los formatos de texto y CSV ". Solo haga esto si tiene millones de filas para insertar y si se siente cómodo con los datos binarios.
Aquí hay una receta de ejemplo en Python, usando psycopg2 con entrada binaria .
Depende principalmente de la (otra) actividad en la base de datos. Operaciones como esta congelan efectivamente toda la base de datos para otras sesiones. Otra consideración es el modelo de datos y la presencia de restricciones, disparadores, etc.
Mi primer enfoque es siempre: crear una tabla (temp) con una estructura similar a la tabla de destino (crear la tabla tmp AS select * from target donde 1 = 0), y comenzar leyendo el archivo en la tabla temporal. Luego verifico lo que se puede verificar: duplicados, claves que ya existen en el objetivo, etc.
Luego solo hago un "do insert en target select * from tmp" o similar.
Si esto falla o toma demasiado tiempo, lo aborto y considero otros métodos (descartar temporalmente índices / restricciones, etc.)
Implementé un cargador de datos Postgresq muy rápido con métodos nativos de libpq. Pruebe mi paquete https://www.nuget.org/packages/NpgsqlBulkCopy/
Acabo de encontrar este problema y recomendaría csvsql ( lanzamientos ) para importaciones masivas a Postgres. Para realizar una inserción masiva, simplemente createdb
usaría y luego usaría csvsql
, que se conecta a su base de datos y crea tablas individuales para una carpeta completa de CSV.
$ createdb test
$ csvsql --db postgresql:///test --insert examples/*.csv
El término "datos masivos" está relacionado con "una gran cantidad de datos", por lo que es natural utilizar datos originales sin procesar , sin necesidad de transformarlos en SQL. Los archivos de datos en bruto típicos para "inserción masiva" son CSV y JSON formatos .
En aplicaciones ETL y procesos de ingestión, necesitamos cambiar los datos antes de insertarlos. La tabla temporal consume (mucho) espacio en disco, y no es la forma más rápida de hacerlo. El contenedor de datos foráneos PostgreSQL (FDW) es la mejor opción.
Ejemplo de CSV . Supongamos que tablename (x, y, z)
en SQL y un archivo CSV como
fieldname1,fieldname2,fieldname3
etc,etc,etc
... million lines ...
Puede usar el SQL clásico COPY
para cargar ( como son los datos originales) tmp_tablename
, insertar datos filtrados en tablename
... Pero, para evitar el consumo de disco, lo mejor es ingerir directamente por
INSERT INTO tablename (x, y, z)
SELECT f1(fieldname1), f2(fieldname2), f3(fieldname3) -- the transforms
FROM tmp_tablename_fdw
-- WHERE condictions
;
Debe preparar la base de datos para FDW y, en su lugar, estática tmp_tablename_fdw
, puede usar una función que la genere :
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 ejemplo . Un conjunto de dos archivos, myRawData1.json
y Ranger_Policies2.json
puede ser ingerido por:
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
;
donde la función jsonb_read_files () lee todos los archivos de una carpeta, definida por una máscara:
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;
El método más frecuente para la "ingestión de archivos" (principalmente en Big Data) es preservar el archivo original en formato gzip y transferirlo con un algoritmo de transmisión , cualquier cosa que pueda ejecutarse rápidamente y sin consumo de disco en tuberías Unix:
gunzip remote_or_local_file.csv.gz | convert_to_sql | psql
Así que ideal (futuro) es una opción de servidor para el formato .csv.gz
.