CSVファイル(customers.csv
)をMySQLテーブル(customers
)にインポートするPHPスクリプトに取り組んでいます。
MysqlテーブルにCSVファイルの内容を挿入する前に、最初に元のcustomers
テーブルをバックアップしています。
インポートプロセス全体(バックアップを含む)をmysqlトランザクションでラップしています(CSVが途中で破損している場合を考慮し、インポートがアトミックであることを確認するため)。
問題はROLLBACKはINSERT INTO
ステートメントの直後に呼び出すと機能しないようです。phpMyAdminを介してデータベースを確認すると、新しく作成されたテーブルとROWS INSIDE ITがロールバック後も存在することがわかります。
操作のログは次のとおりです。
[2015-01-19 14:08:11] DEBUG: "START TRANSACTION" [] []
[2015-01-19 14:08:11] DEBUG: SHOW TABLES LIKE :table_name; [] []
[2015-01-19 14:08:28] DEBUG: CREATE TABLE `customers__20150119_14_08_20` LIKE `customers` [] []
[2015-01-19 14:08:37] DEBUG: INSERT INTO `customers__20150119_14_08_20` SELECT * FROM `customers` [] []
[2015-01-19 14:08:50] DEBUG: "ROLLBACK" [] []
なぜdepsite ROLLBACK
が呼び出されたのか、トランザクションはキャンセルされません。 CREATE TABLE
は本来トランザクションではなく、ロールバックできないことを理解しています。しかし、私はINSERT INTO
が行の挿入(スキーマの定義ではない)を処理するため、実際にはトランザクションであり、ROLLBACKの後は空の宛先テーブルが残ると想定していました。なぜそうではないのですか?
そして、ここに出力SHOW CREATE TABLE customers
があります(つまり、私のテーブルはInnoDb
です):
CREATE TABLE `customers` (
`Code` varchar(32) NOT NULL,
`Name` varchar(128) DEFAULT NULL,
`Price` varchar(128) DEFAULT NULL,
PRIMARY KEY (`Code`),
KEY `Price` (`Price`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
そしてここに宛先テーブルの出力があります:
CREATE TABLE `customers__20150119_14_08_20` (
`Code` varchar(32) NOT NULL,
`Name` varchar(128) DEFAULT NULL,
`Price` varchar(128) DEFAULT NULL,
PRIMARY KEY (`Code`),
KEY `Price` (`Price`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
その理由は、CREATE TABLE
などの一部のステートメントが暗黙のコミットを引き起こすためです。それらについてはドキュメントで読むことができます: 暗黙のコミットを引き起こすステートメント 。
したがって、ステートメントの元のシーケンス:
START TRANSACTION
SHOW TABLES LIKE customers
CREATE TABLE `customers__20150119_14_08_20` LIKE `customers`
INSERT INTO `customers__20150119_14_08_20` SELECT * FROM `customers`
ROLLBACK
に展開されます:
START TRANSACTION ; -- transaction context created
SHOW TABLES LIKE customers ;
COMMIT ; -- CREATE TABLE forces commit before itself
-- (at this point the previous transaction is done.)
START TRANSACTION ; -- and a new transaction
CREATE TABLE `customers__20150119_14_08_20`
LIKE `customers` ;
COMMIT ; -- CREATE TABLE forces commit after itself.
-- At this point there's no transaction context
START TRANSACTION ; -- starts a new transaction
INSERT INTO `customers__20150119_14_08_20`
SELECT * FROM `customers` ;
COMMIT ; -- caused by "autocommit on" setting (guess).
ROLLBACK ; -- this rollback HAS NOTHING to undo
解決策は、CREATE TABLE
ステートメントの後にトランザクション(または新しいトランザクション)を開始するか、一時テーブルを使用することです。
ステートメントの順序が問題を引き起こしているようです。
以前の投稿 ACIDトランザクションinnodb内の行ロック で、トランザクションを断続的に中断する12のステートメントに名前を付けました。あなたの特定のケースでは、それはCREATE TABLE
ステートメントでした。
CREATE TABLE
... START TRANSACTION
ブロック内でCOMMIT/ROLLBACK
を実行すると、ロールバックするフレームワークがありませんでした。
CREATE TABLE
の前にSTART TRANSACTION
を実行するだけで大丈夫です。
試してみる !!!