mysql事务 - 回滚任何异常

Posted

技术标签:

【中文标题】mysql事务 - 回滚任何异常【英文标题】:mysql transaction - roll back on any exception 【发布时间】:2013-11-23 05:28:18 【问题描述】:

如果mysql命令列表出现任何错误,是否可以自动回滚?

例如:

begin transaction;

insert into myTable values1 ...
insert into myTable values2 ...;  -- will throw an error

commit;

现在,在执行时我希望整个事务失败,因此我应该在 myTable 中看到 values1。 但不幸的是,即使事务有错误,该表也被填充了 values1。

任何想法我如何使它回滚? (再次,有任何错误)?

编辑 - 从 DDL 更改为标准 SQL

【问题讨论】:

这是毫无意义的,因为 MySQL 中的事务 do not support DDL 感谢您的评论,我编辑了我的原始帖子 你考虑过使用处理程序吗? 13.6.7.2. DECLARE ... HANDLER Syntax 【参考方案1】:

您可以通过以下方式使用13.6.7.2. DECLARE ... HANDLER Syntax:

DELIMITER $$

CREATE PROCEDURE `sp_fail`()
BEGIN
    DECLARE `_rollback` BOOL DEFAULT 0;
    DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET `_rollback` = 1;
    START TRANSACTION;
    INSERT INTO `tablea` (`date`) VALUES (NOW());
    INSERT INTO `tableb` (`date`) VALUES (NOW());
    INSERT INTO `tablec` (`date`) VALUES (NOW()); -- FAIL
    IF `_rollback` THEN
        ROLLBACK;
    ELSE
        COMMIT;
    END IF;
END$$

DELIMITER ;

如需完整示例,请查看以下SQL Fiddle。

【讨论】:

我的问题是,这个存储过程会永久保存在数据库中吗? @MDaniyal 简短回答:是的。 感谢您的回答 :)。您认为我们为每个新脚本保存一个新的存储过程是个好主意吗?因为我们每个版本有 10 到 15 个脚本,所以我们会有数百个存储过程。 如果异常发生在第1个INSERT,MySQL不会做第2个和第3个,有时会导致意想不到的结果吗? @Xenos 不,虽然会浪费时间。如果前两个查询中的任何一个失败,_rollback 仍设置为 1,因此该函数将执行 ROLLBACK; 而不是 COMMIT;。但是,以下查询仍将执行(事务中),只是在稍后最终回滚。 MySQL 选择了这种继续出错的行为真的让我感到困惑。将此与 PostgreSQL 进行对比,后者将事务置于失败状态,保证事务中的所有未来查询都将失败(ROLLBACK TO 除外)并在提交时隐式回滚。【参考方案2】:

例如,如果您需要在代码中发出特定的 SQL 异常信号,则可以使用 EXIT HANDLER。例如:

DELIMITER $$

CREATE PROCEDURE `sp_fail`()
BEGIN
    DECLARE EXIT HANDLER FOR SQLEXCEPTION
    BEGIN
        ROLLBACK;  -- rollback any changes made in the transaction
        RESIGNAL;  -- raise again the sql exception to the caller
    END;

    START TRANSACTION;
    insert into myTable values1 ...
    IF fail_condition_meet THEN
        SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Custom error detected.', MYSQL_ERRNO = 2000;
    END IF;
    insert into myTable values2 ...  -- this will not be executed
    COMMIT; -- this will not be executed
END$$

DELIMITER ;

【讨论】:

我使用的是 5.2。 RESIGNAL 似乎已在 5.5 中添加。关于如何使用 5.2 获取错误消息的任何建议? 如果您需要记录异常:ROLLBACK 将重置所有异常详细信息,因此您应该在 ROLLBACK 之前设置一些会话变量,并在 ROLLBACK 之后和 RESIGNAL 之前将它们传递给记录过程,例如:@ 987654321@【参考方案3】:

上面的解决方案很好,但更简单

DELIMITER $$

CREATE PROCEDURE `sp_fail`()
BEGIN
    DECLARE EXIT HANDLER FOR SQLEXCEPTION
    BEGIN
        ROLLBACK;  -- rollback any error in the transaction
    END;

    START TRANSACTION;
    insert into myTable values1 ...
    insert into myTable values2 ...  -- Fails
    COMMIT; -- this will not be executed
END$$

DELIMITER ;

【讨论】:

以上是关于mysql事务 - 回滚任何异常的主要内容,如果未能解决你的问题,请参考以下文章

Mysql存储过程中的事务回滚

MySQL数据库—— 事务

Redis:对比一下Redis和MySQL的事务

Redis:对比一下Redis和MySQL的事务

事务嵌套问题总结-rollback-only异常

mysql事务处理失效原因