Necesito insertar varias filas con una consulta (el número de filas no es constante), por lo que necesito ejecutar una consulta como esta:
INSERT INTO t (a, b) VALUES (1, 2), (3, 4), (5, 6);
La única manera que sé es
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)
Pero quiero una manera más simple.
python
postgresql
psycopg2
Sergey Fedoseev
fuente
fuente
execute
estrategia. ¡Vi una aceleración de alrededor de 100x gracias a esto!executemany
ejecuta una confirmación después de cada inserción. Si, en cambio, envuelve todo en una transacción, ¿tal vez eso aceleraría las cosas?executemany
no hace nada óptimo, solo realiza bucles y hace muchasexecute
declaraciones. Con este método, una inserción de 700 filas en un servidor remoto pasó de 60 a <2 segundos.+
parece que podría abrirse a la inyección sql, siento que laexecute_values()
solución @Clodoaldo Neto es más segura.Nuevo
execute_values
método en Psycopg 2.7:La forma pitónica de hacerlo en Psycopg 2.6:
Explicación: Si los datos a insertar se dan como una lista de tuplas como en
entonces ya está en el formato exacto requerido como
la
values
sintaxis de lainsert
cláusula espera una lista de registros como eninsert into t (a, b) values (1, 'x'),(2, 'y')
Psycopg
adapta un Pythontuple
a un Postgresqlrecord
.El único trabajo necesario es proporcionar una plantilla de lista de registros para que sea completada por psycopg
y colocarlo en la
insert
consultaImprimir las
insert_query
salidasAhora a la
Psycopg
sustitución de argumentos habitualesO simplemente probando lo que se enviará al servidor
Salida:
fuente
execute_values
pude hacer que mi sistema funcionara con 1k registros por minuto hasta 128k registros por minutoActualización con psycopg2 2.7:
El clásico
executemany()
es aproximadamente 60 veces más lento que la implementación de @ ant32 (llamada "plegada") como se explica en este hilo: https://www.postgresql.org/message-id/20170130215151.GA7081%40deb76.aryehleib.comEsta implementación se agregó a psycopg2 en la versión 2.7 y se llama
execute_values()
:Respuesta anterior:
Para insertar varias filas, usar la
VALUES
sintaxis de múltiples filasexecute()
es aproximadamente 10 veces más rápido que usar psycopg2executemany()
. De hecho,executemany()
solo corre muchos individuosINSERT
declaraciones .El código de @ ant32 funciona perfectamente en Python 2. Pero en Python 3,
cursor.mogrify()
devuelve bytes,cursor.execute()
toma bytes o cadenas, y','.join()
esperastr
instancia.Entonces, en Python 3, es posible que deba modificar el código de @ ant32, agregando
.decode('utf-8')
:O mediante el uso de bytes (con
b''
ob""
) solamente:fuente
cursor.copy_from es la solución más rápida que he encontrado para las inserciones masivas con diferencia. Aquí hay un resumen que hice que contiene una clase llamada IteratorFile que permite que un iterador que produce cadenas se lea como un archivo. Podemos convertir cada registro de entrada en una cadena usando una expresión generadora. Entonces la solución sería
Para este tamaño trivial de args, no habrá mucha diferencia de velocidad, pero veo grandes aceleraciones cuando se trata de miles de filas. También será más eficiente en la memoria que construir una cadena de consulta gigante. Un iterador solo mantendría un registro de entrada en la memoria a la vez, donde en algún momento se quedará sin memoria en su proceso de Python o en Postgres al construir la cadena de consulta.
fuente
Un fragmento de la página del tutorial de Psycopg2 en Postgresql.org (ver abajo) :
No guarda mucho código, pero definitivamente se ve mejor.
fuente
INSERT
declaraciones individuales . Útil, pero no es lo mismo que una solaVALUE
inserción multid.Todas estas técnicas se llaman 'Inserciones extendidas "en la terminología de Postgres y, a partir del 24 de noviembre de 2016, sigue siendo mucho más rápido que executemany () de psychopg2 y todos los demás métodos enumerados en este hilo (que probé antes de llegar a este responder).
Aquí hay un código que no usa cur.mogrify y es agradable y simplemente para entenderlo:
Pero debe tenerse en cuenta que si puede usar copy_from (), debe usar copy_from;)
fuente
He estado usando la respuesta de ant32 anterior durante varios años. Sin embargo, he encontrado que hay un error en Python 3 porque
mogrify
devuelve una cadena de bytes.La conversión explícita a cadenas bytse es una solución simple para hacer que el código python 3 sea compatible.
fuente
Otro enfoque agradable y eficiente es pasar filas para la inserción como 1 argumento, que es una matriz de objetos json.
Por ejemplo, tu argumento pasajero:
Es una matriz, que puede contener cualquier cantidad de objetos en su interior. Entonces su SQL se ve así:
Aviso: su postgreso debe ser lo suficientemente nuevo para admitir json
fuente
La solución cursor.copyfrom proporcionada por @ jopseph.sheedy ( https://stackoverflow.com/users/958118/joseph-sheedy ) anterior ( https://stackoverflow.com/a/30721460/11100064 ) es realmente muy rápida.
Sin embargo, el ejemplo que da no se puede usar genéricamente para un registro con cualquier número de campos y me tomó un tiempo descubrir cómo usarlo correctamente.
El
r
archivo Iterator necesita ser instanciado con campos separados por tabuladores como este ( es una lista de dictados donde cada dict es un registro):Para generalizar para un número arbitrario de campos, primero crearemos una cadena de línea con la cantidad correcta de pestañas y marcadores de posición de campo:
"{}\t{}\t{}....\t{}"
y luego la usaremos.format()
para completar los valores de campo*list(r.values())) for r in records
:Función completa en esencia aquí .
fuente
Si está utilizando SQLAlchemy, no necesita meterse a mano en la elaboración de la cadena porque SQLAlchemy admite la generación de una
VALUES
cláusula de varias filas para una solaINSERT
declaración :fuente
insert_query
línea. Entonces,session.execute()
solo llama a laexecute()
declaración de psycopg2 con una sola cadena masiva. Entonces, el "truco" es construir primero todo el objeto de la declaración de inserción. Estoy usando esto para insertar 200,000 filas a la vez y vi aumentos masivos de rendimiento usando este código en comparación con lo normalexecutemany()
.ejecutar_batch se ha agregado a psycopg2 desde que se publicó esta pregunta.
Es más lento que execute_values pero más simple de usar.
fuente
execute_values
es más rápido queexecute_batch
Muchos de ellos aceptan un conjunto de tuplas
https://www.postgresqltutorial.com/postgresql-python/insert/
fuente
Si desea insertar varias filas dentro de un estado de inserción (suponiendo que no esté utilizando ORM), la forma más fácil hasta ahora para mí sería utilizar la lista de diccionarios. Aquí hay un ejemplo:
Como puede ver, solo se ejecutará una consulta:
fuente
Usando aiopg : el fragmento a continuación funciona perfectamente bien
fuente
Finalmente en la versión SQLalchemy1.2, esta nueva implementación se agrega para usar psycopg2.extras.execute_batch () en lugar de executemany cuando inicializa su motor con use_batch_mode = True como:
http://docs.sqlalchemy.org/en/latest/changelog/migration_12.html#change-4109
Entonces alguien tendría que usar SQLalchmey no se molestará en probar diferentes combinaciones de sqla y psycopg2 y dirigir SQL juntos.
fuente