MySQLdb.cursor.execute无法运行多个查询

时间:2013-12-11 12:05:09

标签: python mysql mysql-python

我们尝试将包含多个插入语句的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中运行它们?

7 个答案:

答案 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语句,但对我有用。

https://pypi.python.org/pypi/sqlparse

答案 6 :(得分:-2)

使用下面的订单项执行语句:

cursor.execute(query,multi = True)中的_:通过