【问题标题】:What happens by default if an error occurs during a transaction in a stored procedure?如果在存储过程中的事务期间发生错误,默认情况下会发生什么?
【发布时间】:2021-06-02 17:24:51
【问题描述】:

假设您有一个如下所示的存储过程:

CREATE OR REPLACE
PROCEDURE usp_do_something (
    IN param1 INT,
    IN param2 INT,
    IN param3 INT
)
MODIFIES SQL DATA
BEGIN
    START TRANSACTION;

    INSERT `table1` ( `ID` ) VALUES (param1);
    INSERT `table2` ( `ID` ) VALUES (param2);
    INSERT `table3` ( `ID` ) VALUES (param3);

    COMMIT;
END;

然后使用一组参数值调用存储过程,这会导致INSERT 操作之一失败,从而引发SQLEXCEPTION

此示例中没有明确的ROLLBACK 命令。测试表明,当COMMIT; 语句之前出现SQLEXCEPTION 时,事务未提交。存储过程结束时事务是否隐式回滚?或者事务是否会一直挂起,直到发生某种超时?这会导致存储过程在失败后保持锁定一段时间吗?

我知道我可以使用DECLARE HANDLER 在失败时显式触发ROLLBACK,如this question 中所述,但我找不到任何文档描述如果存储过程事务在到达@987654329 之前失败,MariaDB 会做什么没有任何明确的ROLLBACK 的@ 语句。

是否有 MariaDB/MySQL 日志或事务状态表可供我查看,以了解服务器对该事务执行的操作?

【问题讨论】:

    标签: mysql stored-procedures transactions mariadb


    【解决方案1】:

    当存储过程失败时执行被中止,但事务IS NOT FINALIZED

    看一个例子:

    CREATE TABLE table1 (ID INT CHECK (id < 100));
    CREATE TABLE table2 LIKE table1;
    CREATE TABLE table3 LIKE table1;
    
    CREATE PROCEDURE usp_do_something (
        IN param1 INT,
        IN param2 INT,
        IN param3 INT
    )
    MODIFIES SQL DATA
    BEGIN
        START TRANSACTION;
    
        INSERT `table1` ( `ID` ) VALUES (param1);
        INSERT `table2` ( `ID` ) VALUES (param2);
        INSERT `table3` ( `ID` ) VALUES (param3);
    
        COMMIT;
    END;
    

    插入数据没有错误。已插入所有数据。

    CALL usp_do_something (10, 20, 30);
    
    SELECT *, '1' tablenum FROM table1 UNION ALL
    SELECT *, '2' tablenum FROM table2 UNION ALL
    SELECT *, '3' tablenum FROM table3 ORDER BY ID;
    
    身份证 |表号 -: | :-------- 10 | 1 20 | 2 30 | 3

    在第二条语句中插入错误的数据。插入错误 (40) 之前的所有数据。

    CALL usp_do_something (40, 500, 60);
    
    检查约束“table2_chk_1”被违反。
    SELECT *, '1' tablenum FROM table1 UNION ALL
    SELECT *, '2' tablenum FROM table2 UNION ALL
    SELECT *, '3' tablenum FROM table3 ORDER BY ID;
    
    身份证 |表号 -: | :-------- 10 | 1 20 | 2 30 | 3 40 | 1

    在第二条语句中插入错误的数据。插入错误 (70) 之前的所有数据。由于未提交的事务,最后一次调用可能会被回滚。但是在前一个块(40)中插入的数据是由事务开始隐式提交的,不能回滚。

    CALL usp_do_something (70, 800, 90);
    
    检查约束“table2_chk_1”被违反。
    SELECT *, '1' tablenum FROM table1 UNION ALL
    SELECT *, '2' tablenum FROM table2 UNION ALL
    SELECT *, '3' tablenum FROM table3 ORDER BY ID;
    
    ROLLBACK;
    
    SELECT *, '1' tablenum FROM table1 UNION ALL
    SELECT *, '2' tablenum FROM table2 UNION ALL
    SELECT *, '3' tablenum FROM table3 ORDER BY ID;
    
    身份证 |表号 -: | :-------- 10 | 1 20 | 2 30 | 3 40 | 1 70 | 1 ✓ 身份证 |表号 -: | :-------- 10 | 1 20 | 2 30 | 3 40 | 1

    db小提琴here

    【讨论】:

    • 谢谢,这很有启发性。您能否指出我可以阅读的任何参考材料,以更好地了解在事务未完成/待处理时我应该期望数据库处于什么状态?在第一次存储过程调用后,我很惊讶地看到 (40) 的值;我没有意识到新事务开始时会有隐式 COMMIT 。在我之前通过 phpMyAdmin 调用存储过程的测试中,我似乎得到了不同的结果,没有可观察到的部分插入。也许 phpMyAdmin 正在代表我进行隐式回滚。
    • @Hydrargyrum 启用 MySQL 常规日志,重复您的实验并查找从 phpMyAdmin 发送的查询 - 您会发现其他查询,可能是那些导致提交或回滚的查询。 phpMyAdmin 也有可能只是关闭一个连接并打开另一个连接 - 连接关闭(没有提交)回滚所有更改。
    猜你喜欢
    • 2016-05-19
    • 1970-01-01
    • 2012-03-22
    • 2019-06-02
    • 2011-06-02
    • 2023-04-04
    • 2011-07-23
    • 2021-04-20
    相关资源
    最近更新 更多