Pandas: to_sql es demasiado lento

Creado en 1 feb. 2017  ·  24Comentarios  ·  Fuente: pandas-dev/pandas

Ejemplo de código,

df_name.to_sql('table_name',
                          schema = 'public',
                          con = engine,
                          index = False,
                          if_exists = 'replace')

Descripción del problema

Estoy escribiendo un marco de datos de 500.000 filas en una base de datos de AWS de postgres y lleva mucho, mucho tiempo enviar los datos.

Es un servidor SQL bastante grande y mi conexión a Internet es excelente, por lo que he descartado que contribuyan al problema.

En comparación, csv2sql o usar cat y canalizar a psql en la línea de comandos es mucho más rápido.

IO SQL Usage Question

Comentario más útil

Agregue este código debajo de engine = create_engine(connection_string) :

from sqlalchemy import event

@event.listens_for(e, 'before_cursor_execute')
def receive_before_cursor_execute(conn, cursor, statement, params, context, executemany):
    if executemany:
        cursor.fast_executemany = True
        cursor.commit()

En mi código, la función to_sql tardaba 7 minutos en ejecutarse, y ahora solo tarda 5 segundos;)

Todos 24 comentarios

ver aquí: http://stackoverflow.com/questions/33816918/write-large-pandas-dataframes-to-sql-server-database

con SQLServer necesita importar a través de csv con una carga masiva para mayor eficiencia

ODO no funcionaría para mí, genera errores que no pude corregir, pero d6tstack funcionó bien https://github.com/d6t/d6tstack/blob/master/examples-sql.ipynb. Puede preprocesar con pandas y usa postgres COPY FROM para hacer la importación rápida. Funciona bien con RDS postgres.

Agregue este código debajo de engine = create_engine(connection_string) :

from sqlalchemy import event

@event.listens_for(e, 'before_cursor_execute')
def receive_before_cursor_execute(conn, cursor, statement, params, context, executemany):
    if executemany:
        cursor.fast_executemany = True
        cursor.commit()

En mi código, la función to_sql tardaba 7 minutos en ejecutarse, y ahora solo tarda 5 segundos;)

¡Gracias @llautert!
¡Eso ayudó mucho!

# dont forget to import event
from sqlalchemy import event, create_engine

engine = create_engine(connection_string)

@event.listens_for(engine, 'before_cursor_execute')
def receive_before_cursor_execute(conn, cursor, statement, params, context, executemany):
    if executemany:
        cursor.fast_executemany = True
        cursor.commit()

Intenté ejecutar esta solución, pero me encontré con un mensaje de error:

AttributeError: 'psycopg2.extensions.cursor' object has no attribute 'fast_executemany'

Alguien sabe que esta pasando?

Hola @ tim-sauchuk, también me encontré con el mismo error, aunque encontré una solución que ha funcionado muy bien, que implica una ligera edición en el archivo pandas.io.sql.py (simplemente elimine el archivo .pyc de __pycache__ antes de importar de nuevo para asegurarse de que escribe la nueva versión en el archivo comprimido)

https://github.com/pandas-dev/pandas/issues/8953

Hola @ tim-sauchuk, también me encontré con el mismo error, aunque encontré una solución que ha funcionado muy bien que implica una ligera edición en el archivo pandas.io.sql.py (simplemente elimine el archivo .pyc de

8953

El problema # 8953 que @ bsaunders23 mencionó también muestra una forma de "parchear" (arreglarlo en tiempo de ejecución). Lo probé y un conjunto de datos de 20k que tardó más de 10 minutos en cargarse y luego solo tomó 4 segundos.

¡Gracias @llautert!
¡Eso ayudó mucho!

# dont forget to import event
from sqlalchemy import event, create_engine

engine = create_engine(connection_string)

@event.listens_for(engine, 'before_cursor_execute')
def receive_before_cursor_execute(conn, cursor, statement, params, context, executemany):
    if executemany:
        cursor.fast_executemany = True
        cursor.commit()

¿Alguien sabe cómo puedo implementar esta solución dentro de una clase con una instancia de self.engine?

¿Alguien sabe cómo puedo implementar esta solución dentro de una clase con una instancia de self.engine?

Funciona para mí refiriéndose a self.engine

Ejemplo:

    self.engine = sqlalchemy.create_engine(connectionString, echo=echo)
    self.connection = self.engine.connect()

    @event.listens_for(self.engine, 'before_cursor_execute')
    def receive_before_cursor_execute(conn, cursor, statement, params, context, executemany):
        print("Listen before_cursor_execute - executemany: %s" % str(executemany))
        if executemany:
            cursor.fast_executemany = True
            cursor.commit()

No me funciona. ¿Qué versión de pandas y sqlalchemy estás usando?

Lo probé ejecutando sqlalchemy: 1.2.4-py35h14c3975_0 y 1.2.11-py35h7b6447c_0

pero estoy consiguiendo

AttributeError: el objeto 'psycopg2.extensions.cursor' no tiene atributo 'fast_executemany'

@ dean12 @llautert

¿Cómo se ve la llamada a la función en este contexto? O en otras palabras, ¿qué está usando para que los argumentos carguen correctamente la tabla?

<# dont forget to import event
from sqlalchemy import event, create_engine

engine = create_engine(connection_string)

@event.listens_for(engine, 'before_cursor_execute')
def receive_before_cursor_execute(conn, cursor, statement, params, context, executemany):
    if executemany:
        cursor.fast_executemany = True
        cursor.commit()>``

Lo probé ejecutando sqlalchemy: 1.2.4-py35h14c3975_0 y 1.2.11-py35h7b6447c_0

pero estoy consiguiendo

AttributeError: el objeto 'psycopg2.extensions.cursor' no tiene atributo 'fast_executemany'

Está utilizando psycopg2, que es un controlador de postgresql. Este problema y la solución pertenecen a Microsoft SQL Server mediante el controlador pyodbc.

¿qué pasa con agregar el parámetro 'dtype'

¿Alguien sabe cómo puedo implementar esta solución dentro de una clase con una instancia de self.engine?

Funciona para mí refiriéndose a self.engine

Ejemplo:

    self.engine = sqlalchemy.create_engine(connectionString, echo=echo)
    self.connection = self.engine.connect()

    @event.listens_for(self.engine, 'before_cursor_execute')
    def receive_before_cursor_execute(conn, cursor, statement, params, context, executemany):
        print("Listen before_cursor_execute - executemany: %s" % str(executemany))
        if executemany:
            cursor.fast_executemany = True
            cursor.commit()

¿Has averiguado cómo?

Creo que la respuesta correcta debería ser usar https://docs.sqlalchemy.org/en/13/dialects/postgresql.html#psycopg2 -batch-mode-fast-execution, si está tratando de ahorrar pandas dataframe a postgres

Una nueva versión de pandas contiene el parámetro method que podría elegirse para ser 'múltiple'. Esto hace que el código se ejecute mucho más rápido.

fast_executemany se puede realizar en un solo paso ahora (sqlalchemy> = 1.3.0):

engine = sqlalchemy.create_engine(connection_string, fast_executemany=True)

¿Quizás vale la pena mencionarlo en algún lugar de los documentos o con un ejemplo? Es un caso particular no relacionado con los pandas, pero es una pequeña adición que podría mejorar drásticamente el rendimiento en muchos casos.

Una nueva versión de pandas contiene el parámetro method que podría elegirse para ser 'múltiple'. Esto hace que el código se ejecute mucho más rápido.

Pensaría que establecer el parámetro chunksize sería suficiente para hacer una inserción por lotes de to_sql pero no.

Una alternativa para los usuarios de MS SQL es usar también turbodbc.Cursor.insertmanycolumns , lo he explicado en la publicación vinculada de StackOverflow: https://stackoverflow.com/a/62671681/1689261

Para los futuros lectores sobre esto, hay dos opciones para usar un 'batch_mode' para to_sql. Las siguientes son las dos combinaciones:

create_engine(connection_string, executemany_mode='batch', executemany_batch_page_size=x)

o

create_engine(connection_string, executemany_mode='values', executemany_values_page_size=x)

Los detalles sobre estos argumentos se pueden encontrar aquí: https://docs.sqlalchemy.org/en/13/dialects/postgresql.html#psycopg2 -fast-execution-helpers

Para los usuarios de postgres, recomiendo configurar method en un invocable:

invocable con firma (pd_table, conn, keys, data_iter): esto se puede usar para implementar un método de inserción más eficaz basado en características específicas del dialecto de backend.

y llame a la función desde el código de ejemplo aquí https://pandas.pydata.org/pandas-docs/stable/user_guide/io.html#insertion -method y

Usar COPY FROM es mucho más rápido 🚀

¿Fue útil esta página
0 / 5 - 0 calificaciones