2010-02-16 21 views
33

Estoy buscando la manera más eficiente de insertar a granel algunos millones de tuplas en una base de datos. Estoy usando Python, PostgreSQL y psycopg2.Psycopg2, Postgresql, Python: la forma más rápida de insertar de forma masiva

He creado una larga lista de tulipanes que deben insertarse en la base de datos, a veces con modificadores geométricos como Simplify.

La forma ingenua de hacerlo sería cadena de formato de una lista de INSERT declaraciones, pero hay otros tres métodos que he leído sobre: ​​

  1. Uso pyformat binding style para la inserción paramétrica
  2. executemany Uso de la lista de tuplas, y
  3. Usando escribir los resultados en un archivo y usando COPY.

Parece que la primera forma es la más eficiente, pero agradecería sus ideas y fragmentos de código diciéndome cómo hacerlo bien.

Respuesta

13

Sí, votaría por COPY, siempre que pueda escribir un archivo en el disco del servidor del disco duro (no en la unidad en la que se ejecuta la aplicación), ya que COPY solo leerá el servidor.

+19

Usando el cursor.copy_ del archivo pscopg2 es manejado por el cliente. Ni siquiera necesita ser un archivo de sistema de archivos: cualquier objeto similar a un archivo python funciona bien. Consulte http://initd.org/psycopg/docs/cursor.html#cursor.copy_from – piro

+1

En ese caso, sería interesante ver cómo se ha insertado realmente en la base de datos. Tenía la impresión de que COPY de PostgreSQL solo leía desde el servidor. sistema de archivos local y no había forma de copiar un archivo a granel utilizando el cliente. –

+5

También puede leer 'STDIN', que significa que los datos provienen de la aplicación del cliente. Consulte los documentos del comando de copia: http://www.postgresql.org/docs/8.4/static/sql-copy.html – piro

1

El primero y el segundo se usarán juntos, no por separado. El tercero sería el servidor más eficiente, ya que el servidor haría todos el trabajo duro.

+0

¿Puede vincular algunas muestras de código? No puedo encontrar ningún buen recurso psycopg2 en la web. –

+0

Psycopg tiene un nuevo manual: hay muchos ejemplos en él ahora. http://initd.org/psycopg/ – piro

+1

Genial, gracias. Encontré otro buen ejemplo aquí: http://www.devx.com/opensource/Article/29071/0/page/3, probablemente el mejor recurso práctico sobre psycopg2 que hay. –

10

Hay un nuevo psycopg2 manual que contiene ejemplos de todas las opciones.

La opción COPY es la más eficiente. Entonces el ejecutormany. Luego, ejecuta con pyformat.

6

Usted podría utilizar a new upsert library:

$ pip install upsert 

(puede que tenga que pip install decorator primero)

conn = psycopg2.connect('dbname=mydatabase') 
cur = conn.cursor() 
upsert = Upsert(cur, 'mytable') 
for (selector, setter) in myrecords: 
    upsert.row(selector, setter) 

Dónde selector es un objeto dict como {'name': 'Chris Smith'} y setter es una dict como { 'age': 28, 'state': 'WI' }

Es casi tan rápido como escribir código personalizado INSERTAR [/ ACTUALIZAR] y ejecutarlo directamente con psycopg2 ... y no explotará si la fila ya existe.

+2

Hoy en día, debe usar la implementación interna de PostgreSQL (requiere la versión 9.5+), que consiste en usar 'INSERTAR EN CONFLICTO [...]' https://www.postgresql.org/docs/9.5/ static/sql-insert.html – Rmatt

7

en mi experiencia executemany no es más rápido que correr muchas inserciones mismo, la manera más rápida es para dar formato a una sola INSERT con muchos valores a ti mismo, quizás en el futuro executemany mejorará pero por ahora es bastante lento

i subclase a list y la sobrecarga del método de agregación, por lo que cuando una la lista alcanza un determinado tamaño i formato al INSERT para ejecutarlo

+1

Votación al alza: tu experiencia está verificada en http://stackoverflow.com/questions/8134602/psycopg2-insert-multiple-rows-with-one-query –

+0

Además, ese es un buen truco con el subclase. –

1

Después de algunas pruebas, unnest menudo parece ser una opción muy rápido, ya que aprendí de @Clodoaldo Neto's answer a una pregunta similar.

data = [(1, 100), (2, 200), ...] # list of tuples 

cur.execute("""CREATE TABLE table1 AS 
       SELECT u.id, u.var1 
       FROM unnest(%s) u(id INT, var1 INT)""", (data,)) 

Sin embargo, es can be tricky with extremely large data.

1

Cualquier persona que use sqlalchemy podría probar la versión 1.2, que añade soporte de inserción masiva de usar psycopg2.extras.execute_batch() en lugar de executemany al inicializar el motor con use_batch_mode = True como:

engine = create_engine(
    "postgresql+psycopg2://scott:[email protected]/dbname", 
    use_batch_mode=True) 

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.

Cuestiones relacionadas