我们尝试将包含多个插入语句的SQL文件作为单个查询运行,但是当任何语句包含错误时,rollback
似乎失败。
MySQLd配置:
sql_mode = STRICT_ALL_TABLES
default-storage-engine = innodb
Python代码:
from contextlib import closing
import MySQLdb
database_connection = MySQLdb.connect(host="127.0.0.1", user="root")
with closing(database_connection.cursor()) as cursor:
database_connection.begin()
cursor.execute('DROP DATABASE IF EXISTS db_name')
cursor.execute('CREATE DATABASE db_name')
cursor.execute('USE db_name')
cursor.execute('CREATE TABLE table_name(first_field INTEGER)')
with closing(database_connection.cursor()) as cursor:
try:
database_connection.begin()
cursor.execute('USE db_name')
cursor.execute('INSERT INTO table_name VALUES (1)')
cursor.execute('INSERT INTO table_name VALUES ("non-integer value")')
database_connection.commit()
except Exception as error:
print("Exception thrown: {0}".format(error))
database_connection.rollback()
print("Rolled back")
with closing(database_connection.cursor()) as cursor:
try:
database_connection.begin()
cursor.execute('USE db_name')
cursor.execute('INSERT INTO table_name VALUES (1); INSERT INTO table_name VALUES ("non-integer value")')
database_connection.commit()
except:
print("Exception thrown: {0}".format(error))
database_connection.rollback()
print("Rolled back")
预期结果:“抛出异常”和“回滚”两次打印。
MySQL-python 1.2.4的实际结果:
Exception thrown: (1366, "Incorrect integer value: 'non-integer value' for column 'first_field' at row 1")
Rolled back
Exception thrown: (1366, "Incorrect integer value: 'non-integer value' for column 'first_field' at row 1")
Traceback (most recent call last):
File "test.py", line 30, in <module>
print("Rolled back")
File ".../python-2.7/lib/python2.7/contextlib.py", line 154, in __exit__
self.thing.close()
File ".../virtualenv-python-2.7/lib/python2.7/site-packages/MySQLdb/cursors.py", line 100, in close
while self.nextset(): pass
File ".../virtualenv-python-2.7/lib/python2.7/site-packages/MySQLdb/cursors.py", line 132, in nextset
nr = db.next_result()
_mysql_exceptions.OperationalError: (1366, "Incorrect integer value: 'non-integer value' for column 'first_field' at row 1")
是什么给出的?我们是否真的必须解析SQL以拆分语句(包含所有需要的转义和引用处理)以在多个execute
中运行它们?
答案 0 :(得分:15)
与所有Python DB-API 2.0 implementations一样,cursor.execute()
方法仅设计为一个语句,因为它可以保证之后的光标状态。
请改用cursor.executemany()
method。请注意,as per the DB-API 2.0 specification:
将此方法用于生成一个或多个结果集的操作构成未定义的行为,并且当检测到通过调用已创建结果集创建结果集时,允许(但不要求)实现引发异常。操作
将此用于多个INSERT
语句应该没问题:
cursor.executemany('INSERT INTO table_name VALUES (%s)',
[(1,), ("non-integer value",)]
)
如果您需要从脚本执行一系列不同的语句,那么在大多数情况下,您只需将语句拆分为;
并将每个语句分别提供给cursor.execute()
。
答案 1 :(得分:4)
我认为您在使用多个语句时需要将multi=True
传递给execute
,请参阅http://dev.mysql.com/doc/connector-python/en/connector-python-api-mysqlcursor-execute.html
更新:这适用于mysql.connector
模块,而非本案例中使用的MySQLdb
。
答案 2 :(得分:3)
显然无法在MySQLdb
(又名。MySQL-python
)中执行此操作,因此我们最终只将communicate
数据发送到subprocess.Popen([mysql, ...], stdin=subprocess.PIPE)
并检查{ {1}}。
答案 3 :(得分:1)
尝试了multi=True
方法,但最终通过半分割和循环遍历来分割文件。如果您逃脱了半决赛,显然就不起作用了,但对我来说似乎是最好的方法。
with connection.cursor() as cursor:
for statement in script.split(';'):
if len(statement) > 0:
cursor.execute(statement + ';')
答案 4 :(得分:1)
for _ in cursor.execute(query, multi=True):
pass
这对我有用!
根据文档,这是正确的做法,cursor.execute()返回一个迭代器,只有当消耗了迭代器中的内容时,提交才会成功。
答案 5 :(得分:0)
通过Popen使用mysql
程序肯定会有效,但是如果你只想使用现有连接(和游标),sqlparse
包有一个split
函数会分裂进入陈述。我不确定兼容性是什么,但我有一个脚本:
with open('file.sql', 'rb') as f:
for statement in sqlparse.split(f.read()):
if not statement:
continue
cur.execute(statement)
它只提供了DROP TABLE和CREATE TABLE语句,但对我有用。
答案 6 :(得分:-2)
使用下面的订单项执行语句:
cursor.execute(query,multi = True)中的_:通过