如何回滚 MySQL 事务中的所有语句?
How to rollback all statements inside a MySQL Transaction?
我需要更新一个 table (bigtable
) 的特定列,其中包含另一个 table 的 ID(FK 约束 oldsmalltable
)以指向上的 ID另一个 table(FK 约束 newsmalltable
)。基本上这就是我正在做的事情:
DELIMITER //
CREATE PROCEDURE updatebigtable ()
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION, SQLWARNING ROLLBACK;
START TRANSACTION;
ALTER TABLE bigtable DROP FOREIGN KEY bigtable_ibfk_1,
MODIFY smalltable_id SMALLINT ;
UPDATE bigtable SET smalltable_id=CASE smalltable_id
WHEN 1 THEN 1592
WHEN 2 THEN 1593
WHEN 3 THEN 1602
...
ELSE 0
END;
ALTER TABLE bigtable ADD CONSTRAINT bigtable_ibfk_1
FOREIGN KEY(smalltable_id) REFERENCES newsmalltable(id);
COMMIT;
END//
DELIMITER ;
CALL updatebigtable();
DROP PROCEDURE updatebigtable;
我需要确保如果由于某种原因新的外键约束失败(例如,对于不同类型的列,错误将发生在最后一个 alter table
语句),UPDATE
和第一个 ALTER TABLE
也应该回滚,即它们应该保持原样。
根据 MySQL documentation,通过使用 START TRANSACTION
,该事务禁用了自动提交模式,这将不会允许:
that as soon as you execute a statement that updates (modifies) a table, MySQL stores the update on disk to make it permanent.
我只发现这个问题与我的关系不大:
How can I use transactions in my MySQL stored procedure?
如果我提到的错误发生在事务内部,则之前的语句已经执行并且更新是 "permanently done on disk"...
我也尝试在创建过程之前放置 SET autocommit=0;
但行为仍然相同...我是否遗漏了什么?或者这是 MySQL 事务回滚的预期行为?
如果有任何区别,我正在使用 MySQL v.5.6.17.
ALTER TABLE
语句总是导致隐式提交 (section 13.3.3 from MySQL docs, thanks ),这意味着即使它们在 START TRANSACTION;
... COMMIT;
块中,也有提交次数将与该块内完成的更改次数一样多。
锁定 table 也不是一个选项,因为 (from problems with ALTER TABLE
):
If you use ALTER TABLE
on a transactional table or if you are using Windows, ALTER TABLE
unlocks the table if you had done a LOCK TABLE
on it. This is done because InnoDB and these operating systems cannot drop a table that is in use.
在执行 alter 和 update 语句时避免不需要的 reads/writes 的唯一选择是模拟 ALTER TABLE
:
的所有步骤
- Create a new table named A-xxx with the requested structural changes.
- Copy all rows from the original table to A-xxx.
- Rename the original table to B-xxx.
- Rename A-xxx to your original table name.
- Delete B-xxx.
这样可以在新的 table 中完成更新(在第 2 步之后),并且 bigtable
唯一不可用的时间是在执行第 3 步和第 4 步(重命名)时。
使用 TRY CATCH 块
BEGIN TRAN before BEGIN TRY and ROLLBACK TRAN inside CATCH block
我需要更新一个 table (bigtable
) 的特定列,其中包含另一个 table 的 ID(FK 约束 oldsmalltable
)以指向上的 ID另一个 table(FK 约束 newsmalltable
)。基本上这就是我正在做的事情:
DELIMITER //
CREATE PROCEDURE updatebigtable ()
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION, SQLWARNING ROLLBACK;
START TRANSACTION;
ALTER TABLE bigtable DROP FOREIGN KEY bigtable_ibfk_1,
MODIFY smalltable_id SMALLINT ;
UPDATE bigtable SET smalltable_id=CASE smalltable_id
WHEN 1 THEN 1592
WHEN 2 THEN 1593
WHEN 3 THEN 1602
...
ELSE 0
END;
ALTER TABLE bigtable ADD CONSTRAINT bigtable_ibfk_1
FOREIGN KEY(smalltable_id) REFERENCES newsmalltable(id);
COMMIT;
END//
DELIMITER ;
CALL updatebigtable();
DROP PROCEDURE updatebigtable;
我需要确保如果由于某种原因新的外键约束失败(例如,对于不同类型的列,错误将发生在最后一个 alter table
语句),UPDATE
和第一个 ALTER TABLE
也应该回滚,即它们应该保持原样。
根据 MySQL documentation,通过使用 START TRANSACTION
,该事务禁用了自动提交模式,这将不会允许:
that as soon as you execute a statement that updates (modifies) a table, MySQL stores the update on disk to make it permanent.
我只发现这个问题与我的关系不大:
How can I use transactions in my MySQL stored procedure?
如果我提到的错误发生在事务内部,则之前的语句已经执行并且更新是 "permanently done on disk"...
我也尝试在创建过程之前放置 SET autocommit=0;
但行为仍然相同...我是否遗漏了什么?或者这是 MySQL 事务回滚的预期行为?
如果有任何区别,我正在使用 MySQL v.5.6.17.
ALTER TABLE
语句总是导致隐式提交 (section 13.3.3 from MySQL docs, thanks START TRANSACTION;
... COMMIT;
块中,也有提交次数将与该块内完成的更改次数一样多。
锁定 table 也不是一个选项,因为 (from problems with ALTER TABLE
):
If you use
ALTER TABLE
on a transactional table or if you are using Windows,ALTER TABLE
unlocks the table if you had done aLOCK TABLE
on it. This is done because InnoDB and these operating systems cannot drop a table that is in use.
在执行 alter 和 update 语句时避免不需要的 reads/writes 的唯一选择是模拟 ALTER TABLE
:
- Create a new table named A-xxx with the requested structural changes.
- Copy all rows from the original table to A-xxx.
- Rename the original table to B-xxx.
- Rename A-xxx to your original table name.
- Delete B-xxx.
这样可以在新的 table 中完成更新(在第 2 步之后),并且 bigtable
唯一不可用的时间是在执行第 3 步和第 4 步(重命名)时。
使用 TRY CATCH 块 BEGIN TRAN before BEGIN TRY and ROLLBACK TRAN inside CATCH block