Preciso inserir várias linhas com uma consulta (o número de linhas não é constante), portanto, preciso executar uma consulta como esta:
INSERT INTO t (a, b) VALUES (1, 2), (3, 4), (5, 6);
A única maneira que eu sei é
args = [(1,2), (3,4), (5,6)]
args_str = ','.join(cursor.mogrify("%s", (x, )) for x in args)
cursor.execute("INSERT INTO t (a, b) VALUES "+args_str)
mas eu quero uma maneira mais simples.
python
postgresql
psycopg2
Sergey Fedoseev
fonte
fonte
execute
estratégia. Vi uma aceleração de cerca de 100x graças a isso!executemany
execute uma confirmação após cada inserção. Se você envolver a coisa toda em uma transação, talvez isso acelere as coisas?executemany
não faz nada ideal, apenas faz um loop e faz muitasexecute
declarações. Usando esse método, uma inserção de 700 linhas em um servidor remoto passou de 60 para <2s.+
aparência que possa abrir para injeção de sql, acho que aexecute_values()
solução @Clodoaldo Neto é mais segura.Novo
execute_values
método no Psycopg 2.7:A maneira pitônica de fazer isso no Psycopg 2.6:
Explicação: Se os dados a serem inseridos forem fornecidos como uma lista de tuplas, como em
já está no formato exato necessário, como
a
values
sintaxe dainsert
cláusula espera uma lista de registros como eminsert into t (a, b) values (1, 'x'),(2, 'y')
Psycopg
adapta um Pythontuple
a um Postgresqlrecord
.O único trabalho necessário é fornecer um modelo de lista de registros a ser preenchido pelo psycopg
e coloque-o na
insert
consultaImprimir as
insert_query
saídasAgora, para a
Psycopg
substituição de argumentos usuaisOu apenas testando o que será enviado ao servidor
Resultado:
fonte
execute_values
eu era capaz de obter o meu sistema em execução no 1k registros de um minuto até 128k registros de um minutoAtualize com o psycopg2 2.7:
O clássico
executemany()
é cerca de 60 vezes mais lento que a implementação do @ ant32 (chamada "dobrada"), conforme explicado neste tópico: https://www.postgresql.org/message-id/20170130215151.GA7081%40deb76.aryehleib.comEsta implementação foi adicionada ao psycopg2 na versão 2.7 e é chamada
execute_values()
:Resposta anterior:
Para inserir várias linhas, o uso da
VALUES
sintaxe de múltiplas linhasexecute()
é cerca de 10x mais rápido que o uso do psycopg2executemany()
. Na verdade,executemany()
apenas executa muitosINSERT
declarações .O código do @ ant32 funciona perfeitamente no Python 2. Mas no Python 3,
cursor.mogrify()
retorna bytes,cursor.execute()
pega bytes ou seqüências de caracteres e','.join()
esperastr
instância.Portanto, no Python 3, você pode precisar modificar o código do @ ant32, adicionando
.decode('utf-8')
:Ou usando apenas bytes (com
b''
oub""
):fonte
cursor.copy_from é a solução mais rápida que encontrei para inserções em massa de longe. Aqui está uma síntese que eu criei contendo uma classe chamada IteratorFile que permite que um iterador que produza strings seja lido como um arquivo. Podemos converter cada registro de entrada em uma string usando uma expressão de gerador. Então a solução seria
Para esse tamanho trivial de argumentos, não fará muita diferença de velocidade, mas vejo grandes acelerações ao lidar com milhares de linhas. Também será mais eficiente em termos de memória do que construir uma string de consulta gigante. Um iterador apenas manteria um registro de entrada na memória de cada vez, onde, em algum momento, você ficará sem memória no processo Python ou no Postgres criando a string de consulta.
fonte
Um trecho da página de tutorial do Psycopg2 em Postgresql.org (veja abaixo) :
Não salva muito código, mas definitivamente parece melhor.
fonte
INSERT
instruções individuais . Útil, mas não o mesmo que uma únicaVALUE
inserção múltipla .Todas essas técnicas são chamadas 'Inserções estendidas "na terminologia do Postgres e, em 24 de novembro de 2016, ainda é muito mais rápido que o executemany () do psychopg2 e todos os outros métodos listados neste segmento (que tentei antes de abordar este assunto) responda).
Aqui está um código que não usa cur.mogrify e é legal e simples de entender:
Mas deve-se notar que, se você pode usar copy_from (), deve usar copy_from;)
fonte
Estou usando a resposta do ant32 acima há vários anos. No entanto, descobri que há um erro no python 3 porque
mogrify
retorna uma string de bytes.A conversão explícita em bytes de strings é uma solução simples para tornar o código python 3 compatível.
fonte
Outra abordagem interessante e eficiente - é passar linhas para inserção como 1 argumento, que é uma matriz de objetos json.
Por exemplo, você passando o argumento:
É uma matriz, que pode conter qualquer quantidade de objetos dentro. Então seu SQL se parece com:
Aviso: Seu postgress deve ser novo o suficiente para suportar json
fonte
A solução cursor.copyfrom , fornecida por @ jopseph.sheedy ( https://stackoverflow.com/users/958118/joseph-sheedy ) acima ( https://stackoverflow.com/a/30721460/11100064 ), é realmente muito rápida.
No entanto, o exemplo que ele fornece não é genericamente utilizável para um registro com qualquer número de campos e levei um tempo para descobrir como usá-lo corretamente.
O IteratorFile precisa ser instanciado com campos separados por tabulação como este (
r
é uma lista de dictos em que cada dict é um registro):Para generalizar para um número arbitrário de campos, primeiro criaremos uma sequência de linhas com a quantidade correta de guias e espaços reservados para campos:
"{}\t{}\t{}....\t{}"
e depois usaremos.format()
para preencher os valores dos campos*list(r.values())) for r in records
:função completa em essência aqui .
fonte
Se você estiver usando SQLAlchemy, não precisará mexer na criação manual da sequência, porque o SQLAlchemy suporta a geração de uma
VALUES
cláusula de várias linhas para uma únicaINSERT
instrução :fonte
insert_query
linha. Então,session.execute()
basta chamar aexecute()
declaração do psycopg2 com uma única sequência massiva. Portanto, o "truque" é criar o objeto inteiro da instrução de inserção primeiro. Estou usando isso para inserir 200.000 linhas por vez e vi um desempenho maciço aumentar usando esse código em comparação com o normalexecutemany()
.execute_batch foi adicionado ao psycopg2 desde que esta pergunta foi publicada.
É mais lento que execute_values, mas mais simples de usar.
fonte
execute_values
é mais rápido que oexecute_batch
executar aceita array de tuplas
https://www.postgresqltutorial.com/postgresql-python/insert/
fonte
Se você deseja inserir várias linhas em um status de inserção (supondo que você não esteja usando o ORM), a maneira mais fácil até agora para mim seria usar a lista de dicionários. Aqui está um exemplo:
Como você pode ver, apenas uma consulta será executada:
fonte
Usando aiopg - O trecho abaixo funciona perfeitamente bem
fonte
Finalmente, na versão SQLalchemy1.2, essa nova implementação é adicionada para usar psycopg2.extras.execute_batch () em vez de executemany quando você inicializa seu mecanismo com use_batch_mode = True, como:
http://docs.sqlalchemy.org/en/latest/changelog/migration_12.html#change-4109
Então alguém teria que usar SQLalchmey não se importaria em tentar diferentes combinações de sqla e psycopg2 e direcionar SQL juntos ..
fonte