web-dev-qa-db-ja.com

MySQLdb.cursor.executeは複数のクエリを実行できません

複数の挿入ステートメントを含む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")

予期される結果:「例外がスローされました」と「ロールバックされました」が2回出力されました。

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")

何ができますか?複数のexecutesでステートメントを実行するために、SQLを解析してステートメントを(エスケープと引用処理を伴うすべての手順で)分割する必要がありますか?

16
l0b0

どうやら MySQLdb (aka。_MySQL-python_)ではこれを行う方法がないので、データをcommunicateingしてsubprocess.Popen([mysql, ...], stdin=subprocess.PIPE)returncodeを確認します。

3
l0b0

すべての Python DB-API 2.0実装 と同様に、cursor.execute()メソッドはoneステートメントのみを使用するように設計されています。後でカーソルの状態を保証するためです。

代わりに cursor.executemany() method を使用してください。 DB-API 2.0仕様に従って

1つ以上の結果セットを生成する操作にこのメソッドを使用すると、未定義の動作が構成され、操作の呼び出しによって結果セットが作成されたことを検出すると、実装で例外が発生することが許可されます(必須ではありません)。

これを複数のINSERTステートメントに使用しても問題ありません。

_cursor.executemany('INSERT INTO table_name VALUES (%s)',
    [(1,), ("non-integer value",)]
)
_

スクリプトのように一連の異なるステートメントを実行する必要がある場合は、ほとんどの場合、_;_でステートメントを分割し、各ステートメントをcursor.execute()に個別にフィードするだけで済みます。

17
Martijn Pieters

複数のステートメントを使用するときは、multi=Trueexecuteに渡す必要があると思います。次を参照してください http://dev.mysql.com/doc/connector-python/en/connector-python- api-mysqlcursor-execute.html

更新:これは、この場合に使用されるMySQLdbではなく、mysql.connectorモジュールに適用されます。

4
Johannes

multi=Trueメソッドですが、ファイルをセミで分割してループすることになりました。あなたが準決勝を脱出したならば、明らかに働くつもりはありませんが、私にとっては最善の方法のように思えました。

with connection.cursor() as cursor:
    for statement in script.split(';'):
        if len(statement) > 0:
             cursor.execute(statement + ';')
2
DavidSM

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

0
morganwahl