考虑以下设置,您可以在dbfiddle.uk进行修改:
在第一批中,我们设置了三个简单的表,并为每个表添加一行:
CREATE TABLE t1 (x int NOT NULL);
CREATE TABLE t2 (y int NOT NULL);
CREATE TABLE t3 (z int NOT NULL);
INSERT INTO t1 (x) VALUES (1);
INSERT INTO t2 (y) VALUES (2);
INSERT INTO t3 (z) VALUES (3);
接下来,我们将创建三个存储过程:
DELIMITER //
CREATE PROCEDURE proc1()
BEGIN
UPDATE t1 SET x = x + 1;
END
//
CREATE PROCEDURE proc2()
BEGIN
UPDATE t2 SET y = y - 1;
END
//
CREATE PROCEDURE proc3()
BEGIN
UPDATE t3 SET z = z / 0;
END
///
接下来我们将启动一个事务,运行三个 proc,然后回滚事务。请注意,我们正在显式禁用自动提交,即使这不是严格要求的,因为我们有一个显式事务:
SET autocommit = 0;
START TRANSACTION
CALL proc1();
CALL proc2();
CALL proc3();
ROLLBACK
现在,如果我们检查三个表的内容,我们注意到回滚似乎没有发生。事实上,这些行就像没有事务一样,即看起来好像自动提交已打开。
SELECT *
FROM t1;
SELECT *
FROM t2;
SELECT *
FROM t3;
结果:
╔═══╗ ║×║ ╠═══╣ ║ 2 ║ ╚═══╝ ╔═══╗ ║是║ ╠═══╣ ║ 1 ║ ╚═══╝ ╔═══╗ ║z║ ╠═══╣ ║ 3 ║ ╚═══╝
这里发生了什么,当第三个明显失败时,我如何回滚前两个 proc 的动作?
仅供参考,这已经在 MySQL 5.7.2 上进行了测试,并通过 DBFiddle.uk 在 MySQL 8.0.22 上进行了测试。
似乎 MySQL 在调用时默默地失败了
proc3()
,因此从不运行回滚语句。这就是您应该如何构建代码以实际执行回滚,将一些细节传递回调用者。您可以在dbfiddle.uk测试此代码:
创建原始的三个过程:
创建一个新的 proc 来调用上面的三个 proc:
调用新的 proc,并检查结果:
现在的输出似乎是您所期望的:
还有一个不错的小奖励,您会得到以下输出,提醒您注意问题: