Respostas:
O PostgreSQL tem um guia sobre como preencher melhor um banco de dados inicialmente e sugere o uso do comando COPY para linhas de carregamento em massa. O guia tem outras boas dicas sobre como acelerar o processo, como remover índices e chaves estrangeiras antes de carregar os dados (e adicioná-los novamente depois).
Existe uma alternativa ao uso de COPY, que é a sintaxe dos valores de múltiplas linhas suportada pelo Postgres. A partir da documentação :
INSERT INTO films (code, title, did, date_prod, kind) VALUES
('B6717', 'Tampopo', 110, '1985-02-10', 'Comedy'),
('HG120', 'The Dinner Game', 140, DEFAULT, 'Comedy');
O código acima insere duas linhas, mas você pode estendê-lo arbitrariamente, até atingir o número máximo de tokens de instrução preparados (pode ser US $ 999, mas não tenho 100% de certeza disso). Às vezes, não se pode usar COPY, e esse é um substituto digno para essas situações.
Uma maneira de acelerar as coisas é executar explicitamente várias inserções ou cópias em uma transação (por exemplo, 1000). O comportamento padrão do Postgres é confirmar após cada instrução, portanto, enviando em lotes as confirmações, você pode evitar alguma sobrecarga. Como o guia da resposta de Daniel diz, talvez seja necessário desativar o autocommit para que isso funcione. Observe também o comentário na parte inferior, que sugere aumentar o tamanho dos wal_buffers para 16 MB, também pode ajudar.
UNNEST
A função com matrizes pode ser usada junto com a sintaxe VALUES de múltiplas linhas. Eu acho que esse método é mais lento que o uso, COPY
mas é útil para mim no trabalho com psycopg e python (python list
passado para cursor.execute
torna - se pg ARRAY
):
INSERT INTO tablename (fieldname1, fieldname2, fieldname3)
VALUES (
UNNEST(ARRAY[1, 2, 3]),
UNNEST(ARRAY[100, 200, 300]),
UNNEST(ARRAY['a', 'b', 'c'])
);
sem VALUES
usar a subseleção com verificação de existência 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
);
a mesma sintaxe para atualizações em massa:
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;
Você pode usar o COPY table TO ... WITH BINARY
que é " um pouco mais rápido que os formatos de texto e CSV ". Faça isso apenas se você tiver milhões de linhas para inserir e se estiver confortável com dados binários.
Aqui está uma receita de exemplo em Python, usando psycopg2 com entrada binária .
Depende principalmente da (outra) atividade no banco de dados. Operações como essa congelam efetivamente todo o banco de dados para outras sessões. Outra consideração é o modelo de dados e a presença de restrições, gatilhos, etc.
Minha primeira abordagem é sempre: criar uma tabela (temp) com uma estrutura semelhante à tabela de destino (criar tabela tmp AS select * from target onde 1 = 0) e começar lendo o arquivo na tabela temp. Depois, verifico o que pode ser verificado: duplicatas, chaves que já existem no destino, etc.
Então eu apenas faço um "insira no destino, selecione * de tmp" ou similar.
Se isso falhar ou demorar muito, eu a aboro e considero outros métodos (eliminação temporária de índices / restrições, etc.)
Eu implementei o carregador de dados Postgresq muito rápido com métodos libpq nativos. Experimente o meu pacote https://www.nuget.org/packages/NpgsqlBulkCopy/
Acabei de encontrar esse problema e recomendaria o csvsql ( releases ) para importações em massa do Postgres. Para executar uma inserção em massa, você simplesmente createdb
usaria csvsql
e conectaria ao seu banco de dados e criaria tabelas individuais para uma pasta inteira de CSVs.
$ createdb test
$ csvsql --db postgresql:///test --insert examples/*.csv
O termo "dados em massa" está relacionado a "muitos dados"; portanto, é natural usar dados brutos originais , sem a necessidade de transformá-los em SQL. Arquivos de dados brutos típicos para "inserção em massa" são CSV e JSON formatos .
Nos aplicativos ETL e nos processos de processamento, precisamos alterar os dados antes de inseri-los. A tabela temporária consome (muito) espaço em disco e não é a maneira mais rápida de fazê-lo. O wrapper de dados estrangeiros do PostgreSQL (FDW) é a melhor opção.
Exemplo de CSV . Suponha que tablename (x, y, z)
no SQL e um arquivo CSV como
fieldname1,fieldname2,fieldname3
etc,etc,etc
... million lines ...
Você pode usar o SQL clássico COPY
para carregar ( como os dados originais) tmp_tablename
, inserir dados filtrados em tablename
... Mas, para evitar o consumo de disco, o melhor é ingerir diretamente por
INSERT INTO tablename (x, y, z)
SELECT f1(fieldname1), f2(fieldname2), f3(fieldname3) -- the transforms
FROM tmp_tablename_fdw
-- WHERE condictions
;
Você precisa preparar o banco de dados para o FDW e, em vez estático, tmp_tablename_fdw
pode usar uma função que o gera :
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');
Exemplo JSON . Um conjunto de dois arquivos myRawData1.json
e Ranger_Policies2.json
pode 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
;
onde a função jsonb_read_files () lê todos os arquivos de uma pasta, definida por uma 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;
O método mais frequente para "ingestão de arquivos" (principalmente no Big Data) é preservar o arquivo original no formato gzip e transferi-lo com o algoritmo de streaming , qualquer coisa que possa ser executada rapidamente e sem consumo de disco em pipes unix:
gunzip remote_or_local_file.csv.gz | convert_to_sql | psql
Tão ideal (futuro) é uma opção de servidor para formato .csv.gz
.