web-dev-qa-db-ja.com

INSERT INTOで新しく作成された宛先テーブルの後にROLLBACKが機能しない

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
11
Dimitry K

その理由は、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ステートメントの後にトランザクション(または新しいトランザクション)を開始するか、一時テーブルを使用することです。

13
ypercubeᵀᴹ

ステートメントの順序が問題を引き起こしているようです。

以前の投稿 ACIDトランザクションinnodb内の行ロック で、トランザクションを断続的に中断する12のステートメントに名前を付けました。あなたの特定のケースでは、それはCREATE TABLEステートメントでした。

CREATE TABLE ... START TRANSACTIONブロック内でCOMMIT/ROLLBACKを実行すると、ロールバックするフレームワークがありませんでした。

CREATE TABLEの前にSTART TRANSACTIONを実行するだけで大​​丈夫です。

試してみる !!!

3
RolandoMySQLDBA