psycopg2: inserta varias filas con una consulta

Resuelto Sergey Fedoseev asked hace 12 años • 0 respuestas

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 forma más sencilla.

Sergey Fedoseev avatar Nov 15 '11 17:11 Sergey Fedoseev
Aceptado

Construí un programa que inserta varias líneas en un servidor que estaba ubicado en otra ciudad.

Descubrí que usar este método era aproximadamente 10 veces más rápido que executemany. En mi caso tupes una tupla que contiene alrededor de 2000 filas. Me llevó unos 10 segundos utilizar este método:

args_str = ','.join(cur.mogrify("(%s,%s,%s,%s,%s,%s,%s,%s,%s)", x) for x in tup)
cur.execute("INSERT INTO table VALUES " + args_str) 

y 2 minutos cuando se utiliza este método:

cur.executemany("INSERT INTO table VALUES(%s,%s,%s,%s,%s,%s,%s,%s,%s)", tup)
ant32 avatar Apr 13 '2012 19:04 ant32

Nuevo execute_valuesmétodo en Psycopg 2.7:

data = [(1,'x'), (2,'y')]
insert_query = 'insert into t (a, b) values %s'
psycopg2.extras.execute_values (
    cursor, insert_query, data, template=None, page_size=100
)

La forma pitónica de hacerlo en Psycopg 2.6:

data = [(1,'x'), (2,'y')]
records_list_template = ','.join(['%s'] * len(data))
insert_query = 'insert into t (a, b) values {}'.format(records_list_template)
cursor.execute(insert_query, data)

Explicación: Si los datos a insertar se proporcionan como una lista de tuplas como en

data = [(1,'x'), (2,'y')]

entonces ya está en el formato requerido exacto como

  1. la valuessintaxis de la insertcláusula espera una lista de registros como en

    insert into t (a, b) values (1, 'x'),(2, 'y')

  2. Psycopgadapta un Python tuplea un Postgresql record.

El único trabajo necesario es proporcionar una plantilla de lista de registros para que la complete psycopg.

# We use the data list to be sure of the template length
records_list_template = ','.join(['%s'] * len(data))

y colocarlo en la insertconsulta

insert_query = 'insert into t (a, b) values {}'.format(records_list_template)

Imprimir las insert_querysalidas

insert into t (a, b) values %s,%s

Ahora a la Psycopgsustitución habitual de argumentos.

cursor.execute(insert_query, data)

O simplemente probar lo que se enviará al servidor.

print (cursor.mogrify(insert_query, data).decode('utf8'))

Producción:

insert into t (a, b) values (1, 'x'),(2, 'y')
Clodoaldo Neto avatar Jun 22 '2015 16:06 Clodoaldo Neto

Actualizació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.com

Esta implementación se agregó a psycopg2 en la versión 2.7 y se llama execute_values():

from psycopg2.extras import execute_values
execute_values(cur,
    "INSERT INTO test (id, v1, v2) VALUES %s",
    [(1, 2, 3), (4, 5, 6), (7, 8, 9)])

Respuesta anterior:

Para insertar varias filas, usar la VALUESsintaxis de varias filas con execute()es aproximadamente 10 veces más rápido que usar psycopg2 executemany(). De hecho, executemany()simplemente ejecuta muchas INSERTdeclaraciones individuales.

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()espera struna instancia.

Entonces, en Python 3 es posible que necesites modificar el código de @ant32 agregando .decode('utf-8'):

args_str = ','.join(cur.mogrify("(%s,%s,%s,%s,%s,%s,%s,%s,%s)", x).decode('utf-8') for x in tup)
cur.execute("INSERT INTO table VALUES " + args_str)

O usando bytes (con b''o b""):

args_bytes = b','.join(cur.mogrify("(%s,%s,%s,%s,%s,%s,%s,%s,%s)", x) for x in tup)
cur.execute(b"INSERT INTO table VALUES " + args_bytes) 
Antoine Dusséaux avatar Aug 19 '2016 08:08 Antoine Dusséaux