如果在存储过程中的事务期间发生错误,默认情况下会发生什么情况?
What happens by default if an error occurs during a transaction in a stored procedure?
假设您有一个如下所示的存储过程:
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
命令。测试表明,当 SQLEXCEPTION 发生在 COMMIT;
语句之前时,事务未提交。存储过程结束时事务是否隐式回滚?还是事务会一直挂起直到发生某种超时?这会导致存储过程在失败后保持锁定一段时间吗?
我知道我可以使用 DECLARE HANDLER
在失败时显式触发 ROLLBACK
,如 this question 中所述,但我找不到任何文档来描述 MariaDB 在存储 -在没有任何显式 ROLLBACK
.
的情况下,过程事务在到达 COMMIT
语句之前失败
是否有 MariaDB/MySQL 日志或事务状态 table 可以让我查看服务器正在处理此事务的情况?
当存储过程失败时,执行被中止,但事务未完成。
看例子:
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;
ID | tablenum
-: | :-------
10 | 1
20 | 2
30 | 3
在第二条语句中插入错误数据。插入错误 (40) 之前的所有数据。
CALL usp_do_something (40, 500, 60);
Check constraint 'table2_chk_1' is violated.
SELECT *, '1' tablenum FROM table1 UNION ALL
SELECT *, '2' tablenum FROM table2 UNION ALL
SELECT *, '3' tablenum FROM table3 ORDER BY ID;
ID | tablenum
-: | :-------
10 | 1
20 | 2
30 | 3
40 | 1
在第二条语句中插入错误数据。插入错误 (70) 之前的所有数据。由于未提交的事务,最后一次调用可能会回滚。但是在前一个块(40)中插入的数据是由事务开始隐式提交的,不能回滚。
CALL usp_do_something (70, 800, 90);
Check constraint 'table2_chk_1' is violated.
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;
ID | tablenum
-: | :-------
10 | 1
20 | 2
30 | 3
40 | 1
70 | 1
✓
ID | tablenum
-: | :-------
10 | 1
20 | 2
30 | 3
40 | 1
db<>fiddle here
假设您有一个如下所示的存储过程:
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
命令。测试表明,当 SQLEXCEPTION 发生在 COMMIT;
语句之前时,事务未提交。存储过程结束时事务是否隐式回滚?还是事务会一直挂起直到发生某种超时?这会导致存储过程在失败后保持锁定一段时间吗?
我知道我可以使用 DECLARE HANDLER
在失败时显式触发 ROLLBACK
,如 this question 中所述,但我找不到任何文档来描述 MariaDB 在存储 -在没有任何显式 ROLLBACK
.
COMMIT
语句之前失败
是否有 MariaDB/MySQL 日志或事务状态 table 可以让我查看服务器正在处理此事务的情况?
当存储过程失败时,执行被中止,但事务未完成。
看例子:
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;
ID | tablenum -: | :------- 10 | 1 20 | 2 30 | 3
在第二条语句中插入错误数据。插入错误 (40) 之前的所有数据。
CALL usp_do_something (40, 500, 60);
Check constraint 'table2_chk_1' is violated.
SELECT *, '1' tablenum FROM table1 UNION ALL SELECT *, '2' tablenum FROM table2 UNION ALL SELECT *, '3' tablenum FROM table3 ORDER BY ID;
ID | tablenum -: | :------- 10 | 1 20 | 2 30 | 3 40 | 1
在第二条语句中插入错误数据。插入错误 (70) 之前的所有数据。由于未提交的事务,最后一次调用可能会回滚。但是在前一个块(40)中插入的数据是由事务开始隐式提交的,不能回滚。
CALL usp_do_something (70, 800, 90);
Check constraint 'table2_chk_1' is violated.
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;
ID | tablenum -: | :------- 10 | 1 20 | 2 30 | 3 40 | 1 70 | 1 ✓ ID | tablenum -: | :------- 10 | 1 20 | 2 30 | 3 40 | 1
db<>fiddle here