¿Cuál sería la forma sugerida de ejecutar algo como lo siguiente en python:
self.cursor.execute('SET FOREIGN_KEY_CHECKS=0; DROP TABLE IF EXISTS %s; SET FOREIGN_KEY_CHECKS=1' % (table_name,)) Por ejemplo, ¿deberían ser tres self.cursor.execute(...) separadas? ¿Hay algún método específico que deba usarse además de cursor.execute(...) para hacer algo como esto, o cuál es la práctica sugerida para hacer esto? Actualmente el código que tengo es el siguiente:
self.cursor.execute('SET FOREIGN_KEY_CHECKS=0;') self.cursor.execute('DROP TABLE IF EXISTS %s;' % (table_name,)) self.cursor.execute('SET FOREIGN_KEY_CHECKS=1;') self.cursor.execute('CREATE TABLE %s select * from mytable;' % (table_name,)) Como puede ver, todo se ejecuta por separado... así que no estoy seguro de si es una buena idea o no (o más bien, cuál es la mejor manera de hacer lo anterior). ¿Quizás BEGIN...END ?
En la documentación de MySQLCursor.execute() , sugieren usar el parámetro multi=True :
operation = 'SELECT 1; INSERT INTO t1 VALUES (); SELECT 2' for result in cursor.execute(operation, multi=True): ...Puede encontrar otro ejemplo en el código fuente del módulo.
Me atasqué varias veces en este tipo de problemas en el proyecto. Después de mucha investigación, encontré algunos puntos y sugerencias.
execute() funciona bien con una consulta a la vez. Porque durante el método de ejecución cuida el estado.Sé que
cursor.execute(operation, params=None, multi=True)toma múltiples consultas. Pero los parámetros no funcionan bien en este caso y, a veces, la excepción de error interno también estropea todos los resultados. Y el código se vuelve masivo y ambiguo. Incluso los documentos también mencionan esto.
executemany(operation, seq_of_params) no es una buena práctica para implementar cada vez. Porque la operación que produce uno o más conjuntos de resultados constituye un comportamiento indefinido, y se permite (pero no se requiere) que la implementación genere una excepción cuando detecta que se ha creado un conjunto de resultados mediante una invocación de la operación. [fuente - documentos]Sugerencia 1-:
Haga una lista de consultas como -:
table_name = 'test' quries = [ 'SET FOREIGN_KEY_CHECKS=0;', 'DROP TABLE IF EXISTS {};'.format(table_name), 'SET FOREIGN_KEY_CHECKS=1;', 'CREATE TABLE {} select * from mytable;'.format(table_name), ] for query in quries: result = self.cursor.execute(query) # Do operation with resultSugerencia 2-:
Establecer con dict.
[you can also make this by executemany for recursive parameters for some special cases.]
quries = [ {'DROP TABLE IF EXISTS %(table_name);':{'table_name': 'student'}}, {'CREATE TABLE %(table_name) select * from mytable;': {'table_name':'teacher'}}, {'SET FOREIGN_KEY_CHECKS=0;': ''} ] for data in quries: for query, parameter in data.iteritems(): if parameter == '': result = self.cursor.execute(query) # Do something with result else: result = self.cursor.execute(query, parameter) # Do something with resultTambién puede usar dividir con script.
Not recommended
with connection.cursor() as cursor: for statement in script.split(';'): if len(statement) > 0: cursor.execute(statement + ';')Nota: utilizo principalmente la
list of query, pero en algún lugar complejo uso el enfoque dedictionary.
Mire la documentación de MySQLCursor.execute().
Afirma que puede pasar un parámetro multi que le permite ejecutar múltiples consultas en una cadena.
Si multi se establece en True, execute() puede ejecutar varias declaraciones especificadas en la cadena de operación.
multi es un segundo parámetro opcional para la llamada de ejecución ():
operation = 'SELECT 1; INSERT INTO t1 VALUES (); SELECT 2' for result in cursor.execute(operation, multi=True):Con import mysql.connector
puede hacer el siguiente comando, solo necesita reemplazar t1 y episodios, con sus propios tabaes
tablename= "t1" mycursor.execute("SET FOREIGN_KEY_CHECKS=0; DROP TABLE IF EXISTS {}; SET FOREIGN_KEY_CHECKS=1;CREATE TABLE {} select * from episodes;".format(tablename, tablename),multi=True)Mientras esto se ejecute, debe asegurarse de que las restricciones de clave externa que estarán vigentes después de habilitarlo no causen problemas.
si tablename es algo que un usuario puede ingresar, debe pensar en una lista blanca de nombres de tabla.
Las declaraciones preparadas no funcionan con nombres de tablas y columnas, por lo que tenemos que usar el reemplazo de cadenas para obtener los nombres de tabla correctos en la posición correcta, pero esto hará que su código sea vulnerable a la inyección de sql
El multi=True es necesario para ejecutar 4 comandos en el conector, cuando lo probé, el depurador lo exigió.
Todas las respuestas son completamente válidas, por lo que agregaría mi solución con escritura estática y administrador de contexto de closing .
from contextlib import closing from typing import List import mysql.connector import logging logger = logging.getLogger(__name__) def execute(stmts: List[str]) -> None: logger.info("Starting daily execution") with closing(mysql.connector.connect()) as connection: try: with closing(connection.cursor()) as cursor: cursor.execute(' ; '.join(stmts), multi=True) except Exception: logger.exception("Rollbacking changes") connection.rollback() raise else: logger.info("Finished successfully")Si no me equivoco, es posible que la conexión o el cursor no sean un administrador de contexto, según la versión del controlador mysql que tenga, por lo que es una solución segura para Python.
executescript() Este es un método práctico para ejecutar varias sentencias SQL a la vez. Ejecuta el script SQL que obtiene como parámetro. Sintaxis:
sqlite3.connect.executescript(script)
Código de ejemplo:
import sqlite3 # Connection with the DataBase # 'library.db' connection = sqlite3.connect("library.db") cursor = connection.cursor() # SQL piece of code Executed # SQL piece of code Executed cursor.executescript(""" CREATE TABLE people( firstname, lastname, age ); CREATE TABLE book( title, author, published ); INSERT INTO book(title, author, published) VALUES ( 'Dan Clarke''s GFG Detective Agency', 'Sean Simpsons', 1987 ); """) sql = """ SELECT COUNT(*) FROM book;""" cursor.execute(sql) # The output in fetched and returned # as a List by fetchall() result = cursor.fetchall() print(result) sql = """ SELECT * FROM book;""" cursor.execute(sql) result = cursor.fetchall() print(result) # Changes saved into database connection.commit() # Connection closed(broken) # with DataBase connection.close()Producción:
[(1,)] [("La agencia de detectives GFG de Dan Clarke", 'Sean Simpson', 1987)]
Fuente: GeeksForGeeks: SQL usando Python Echa un vistazo a esta fuente... tiene muchas cosas geniales para ti.
La belleza está en el ojo del espectador, por lo que la mejor manera de hacer algo es subjetiva, a menos que nos digas explícitamente cómo medirla. Hay tres opciones hipotéticas que puedo ver:
multi de MySQLCursor (no ideal)Opcionalmente, también puede cambiar la consulta para evitar un trabajo innecesario.
Con respecto a la opción multi , la documentación de MySQL es bastante clara al respecto.
Si multi se establece en True, execute() puede ejecutar varias declaraciones especificadas en la cadena de operación. Devuelve un iterador que permite procesar el resultado de cada sentencia. Sin embargo, el uso de parámetros no funciona bien en este caso y, por lo general, es una buena idea ejecutar cada instrucción por separado .
Con respecto a la opción 2. y 3. es puramente una preferencia sobre cómo le gustaría ver su código. Recuerde que un objeto de conexión tiene autocommit=FALSE de forma predeterminada, por lo que el cursor en realidad procesa por lotes cursor.execute(...) en una sola transacción. En otras palabras, las dos versiones siguientes son equivalentes.
self.cursor.execute('SET FOREIGN_KEY_CHECKS=0;') self.cursor.execute('DROP TABLE IF EXISTS %s;' % (table_name,)) self.cursor.execute('SET FOREIGN_KEY_CHECKS=1;') self.cursor.execute('CREATE TABLE %s select * from mytable;' % (table_name,))contra
self.cursor.execute( 'SET FOREIGN_KEY_CHECKS=0;' 'DROP TABLE IF EXISTS %s;' % (table_name,) 'SET FOREIGN_KEY_CHECKS=1;' 'CREATE TABLE %s select * from mytable;' % (table_name,) )Python 3.6 introdujo cadenas f que son súper elegantes y deberías usarlas si puedes. :)
self.cursor.execute( 'SET FOREIGN_KEY_CHECKS=0;' f'DROP TABLE IF EXISTS {table_name};' 'SET FOREIGN_KEY_CHECKS=1;' f'CREATE TABLE {table_name} select * from mytable;' )Tenga en cuenta que esto ya no es válido cuando comienza a manipular filas; en este caso, se convierte en una consulta específica y debe generar un perfil si es relevante. Una pregunta SO relacionada es ¿Qué es más rápido, una consulta grande o muchas consultas pequeñas?
Finalmente, puede ser más elegante usar TRUNCATE en lugar de DROP TABLE a menos que tenga razones específicas para no hacerlo.
self.cursor.execute( f'CREATE TABLE IF NOT EXISTS {table_name};' 'SET FOREIGN_KEY_CHECKS=0;' f'TRUNCATE TABLE {table_name};' 'SET FOREIGN_KEY_CHECKS=1;' f'INSERT INTO {table_name} SELECT * FROM mytable;' )Yo crearía un procedimiento almacenado:
DROP PROCEDURE IF EXISTS CopyTable; DELIMITER $$ CREATE PROCEDURE CopyTable(IN _mytable VARCHAR(64), _table_name VARCHAR(64)) BEGIN SET FOREIGN_KEY_CHECKS=0; SET @stmt = CONCAT('DROP TABLE IF EXISTS ',_table_name); PREPARE stmt1 FROM @stmt; EXECUTE stmt1; SET FOREIGN_KEY_CHECKS=1; SET @stmt = CONCAT('CREATE TABLE ',_table_name,' as select * from ', _mytable); PREPARE stmt1 FROM @stmt; EXECUTE stmt1; DEALLOCATE PREPARE stmt1; END$$ DELIMITER ;y luego simplemente ejecuta:
args = ['mytable', 'table_name'] cursor.callproc('CopyTable', args)manteniéndolo simple y modular. Por supuesto, debe realizar algún tipo de comprobación de errores e incluso podría hacer que el procedimiento almacenado devuelva un código para indicar el éxito o el fracaso.