¿Por qué es ejecutable muchos usuarios de Python MySQLdb?

Estoy desarrollando un progtwig en Python que accede a una base de datos MySQL usando MySQLdb. En ciertas situaciones, tengo que ejecutar un comando INSERT o REPLACE en muchas filas. Actualmente lo estoy haciendo así:

db.execute("REPLACE INTO " + table + " (" + ",".join(cols) + ") VALUES" + ",".join(["(" + ",".join(["%s"] * len(cols)) + ")"] * len(data)), [row[col] for row in data for col in cols]) 

Funciona bien, pero es un poco incómodo. Me preguntaba si podría facilitar la lectura, y descubrí el comando de ejecución. Cambié mi código para verse así:

 db.executemany("REPLACE INTO " + table + " (" + ",".join(cols) + ") " + "VALUES(" + ",".join(["%s"] * len(cols)) + ")", [tuple(row[col] for col in cols) for row in data]) 

Todavía funcionaba, pero corría mucho más lento. En mis pruebas, para conjuntos de datos relativamente pequeños (alrededor de 100-200 filas), se ejecutó aproximadamente 6 veces más lento. Para grandes conjuntos de datos (aproximadamente 13,000 filas, la más grande que espero manejar), se ejecutó unas 50 veces más lento. ¿Por qué está haciendo esto?

Realmente me gustaría simplificar mi código, pero no quiero la gran caída en el rendimiento. ¿Alguien sabe de alguna manera de hacerlo más rápido?

Estoy usando Python 2.7 y MySQLdb 1.2.3. Intenté juguetear con la función setinputsizes, pero eso no pareció hacer nada. Miré el código fuente de MySQLdb y parece que no debería hacer nada.

Intente escribir en minúsculas la palabra ‘valores’ en su consulta: esto parece ser un error / regresión en MySQL-python 1.2.3.

La implementación de MySQL-python de executemany () hace coincidir la cláusula VALUES con una expresión regular y luego simplemente clona la lista de valores para cada fila de datos, por lo que termina ejecutando exactamente la misma consulta que con su primer enfoque.

Desafortunadamente, la expresión regular perdió su distintivo que no distingue entre mayúsculas y minúsculas en esa versión (posteriormente se corrigió en el troncal r622, pero nunca se colocó en la twig 1.2), por lo que se degrada a iterar sobre los datos y disparar una consulta por fila.

Su primer ejemplo es una statement única (grande) que se genera y luego se envía a la base de datos.

El segundo ejemplo es una statement mucho más simple que inserta / reemplaza una sola fila pero se ejecuta varias veces. Cada comando se envía a la base de datos por separado, por lo que debe pagar el tiempo de respuesta del cliente al servidor y volver por cada fila insertada. Creo que esta latencia adicional introducida entre los comandos es la razón principal de la disminución del rendimiento del segundo ejemplo.

No recomiendo utilizar executeMany en pyodbc , así como ceodbc tanto lento como contiene muchos errores.

En su lugar, considere usar execute y construir manualmente la consulta SQL utilizando un formato de cadena simple.

 transaction = "TRANSACTION BEGIN {0} COMMIT TRANSACTION bulkRequest = "" for i in range(0, 100) bulkRequest = bulkRequest + "INSERT INTO ...... {0} {1} {2}" ceodbc.execute(transaction.format(bulkRequest)) 

La implementación actual es muy simple, rápida y confiable.

En caso de que esté usando mysqlclient-python (bifurcación de MySQLdb1), también el controlador recomendado para Django (por Django), hay la siguiente información de uso que debe conocer:

cursor.executemany recurre a usar cursor.execute (silenciosamente) en caso de que su consulta sea de la forma:

INSERT INTO testdb.test (type, some_field, status, some_char_field) VALUES (%s, hex(%s), %s, md5(%s));

El controlador emplea una expresión regular de python que no parece admitir el uso de las funciones mysql en la cláusula VALUES .

  RE_INSERT_VALUES = re.compile( r"\s*((?:INSERT|REPLACE)\b.+\bVALUES?\s*)" + r"(\(\s*(?:%s|%\(.+\)s)\s*(?:,\s*(?:%s|%\(.+\)s)\s*)*\))" + r"(\s*(?:ON DUPLICATE.*)?);?\s*\Z", re.IGNORECASE | re.DOTALL) 

Enlace al problema github relevante https://github.com/PyMySQL/mysqlclient-python/issues/334