如果您在主过程中定义了一个捕获所有错误的退出处理程序来回滚,那么当其中的所有过程发送错误消息时,它都会对它们起作用.我们有一个过程n1
,它将数值1到5插入到测试表的PK列中.然后我们有过程n2
,它插入6到10,但随后将数字5插入到表中,这会引发重复的PK值错误.接下来,在我们的主SP nn
中,我们将前面提到的过程放在START TRANSACTION
部分下,并在它们之外添加一些其他东西.
create table test (id int primary key);
delimiter //
drop procedure if exists n1 //
drop procedure if exists n2 //
drop procedure if exists nn //
create procedure n1()
begin
declare n int default 1;
while n<=5 do
insert test values(n);
set n=n+1;
end while;
end//
create procedure n2()
begin
declare n int default 6;
while n<=10 do
insert test values(n);
set n=n+1;
end while;
insert test values(5); -- this raises a dup pk value error
end//
create procedure nn()
begin
declare exit handler for sqlexception
begin
rollback;
resignal;
end;
truncate test;
start transaction ;
insert test values(100);
call n1;
insert test values(200);
call n2;
insert test values(300);
commit;
end//
delimiter ;
让我们调用Main过程:
call nn;
ERROR 1062 (23000): Duplicate entry '5' for key 'PRIMARY'
select * from test;
Empty set (0.00 sec)
如结果所示,整个会话被回滚.
总而言之,在主过程中,如果您已经预定义了一个退出处理程序,以便在发现错误时回滚,则当事务下的所有嵌套过程引发ERR消息时,它将应用于这些过程,这可以被视为一个要么全有要么全不执行的作业(job).
但是,这并不是事情的结束.如果我们将来自子SP的错误通知设置为静音,情况会怎样?例如,我们在n2
中声明了一个退出处理程序,但没有定义RESIGNAL
语句,这将使n2
向外部发送query ok
通知,而不是取消屏蔽真实事件.看看这个:
delimiter //
drop procedure if exists n1 //
drop procedure if exists n2 //
drop procedure if exists nn //
create procedure n1()
begin
declare n int default 1;
while n<=5 do
insert test values(n);
set n=n+1;
end while;
end//
create procedure n2()
begin
declare n int default 6;
declare exit handler for sqlexception begin end;
while n<=10 do
insert test values(n);
set n=n+1;
end while;
insert test values(5); -- this raises a dup pk value error
end//
create procedure nn()
begin
declare exit handler for sqlexception
begin
rollback;
resignal;
end;
truncate test;
start transaction ;
insert test values(100);
call n1;
insert test values(200);
call n2;
insert test values(300);
commit;
end//
delimiter ;
call nn;
Query OK, 0 rows affected (0.02 sec)
select * from test;
+-----+
| id |
+-----+
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
| 6 |
| 7 |
| 8 |
| 9 |
| 10 |
| 100 |
| 200 |
| 300 |
+-----+
如我们所见,由于主SP nn
不再收到错误消息,因此其退出处理程序将不再被触发,因此不会执行回滚.
换句话说,您可以调整子SP,使其工作对您有利,就像我们刚刚做的那样,将来自某些SP的错误消息静音.您甚至可以在不同的SP中自定义错误代码,并在主SP中声明特定的处理程序来处理每个代码.这由您决定.
但请注意,来自嵌套SP的提交或回滚也适用于外部.在下面的简化示例中(这次没有声明条件处理程序),n2
在末尾有一个回滚,该回滚不仅在其自身工作,而且还在外部传播.
delimiter //
drop procedure if exists n1 //
drop procedure if exists n2 //
drop procedure if exists nn //
create procedure n1()
begin
declare n int default 1;
while n<=5 do
insert test values(n);
set n=n+1;
end while;
end//
create procedure n2()
begin
declare n int default 6;
while n<=10 do
insert test values(n);
set n=n+1;
end while;
rollback;
end//
create procedure nn()
begin
truncate test;
start transaction ;
insert test values(100);
call n1;
insert test values(200);
call n2; -- there is a rollback at the end of the procedure
insert test values(300);
commit;
end//
delimiter ;
call nn;
Query OK, 0 rows affected (0.06 sec)
select * from test;
+-----+
| id |
+-----+
| 300 |
+-----+
如上所述,从n2
开始回滚将undo撤消到目前为止事务中的所有更改.
最后,将自动提交设置为关闭时请小心.因为它的效果持续到会话一直持续到切换.当大脑打算执行自动提交操作时,可能会潜在地导致数据丢失.在这方面,使用START TRANSACTION
更安全.
UPDATED with procedure n2.1 thrown in n2个
delimiter //
drop procedure if exists n1 //
drop procedure if exists n2 //
drop procedure if exists `n2.1` //
drop procedure if exists nn //
create procedure n1()
begin
declare n int default 1;
while n<=5 do
insert test values(n);
set n=n+1;
end while;
end//
create procedure `n2.1`()
begin
insert test values(999);
end//
create procedure n2()
begin
declare n int default 6;
call `n2.1`;
while n<=10 do
insert test values(n);
set n=n+1;
end while;
end//
create procedure nn()
begin
truncate test;
start transaction ;
insert test values(100);
call n1;
insert test values(200);
call n2;
insert test values(300);
commit;
end//
delimiter ;
让我们称之为:
call nn;
select * from test;
+-----+
| id |
+-----+
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
| 6 |
| 7 |
| 8 |
| 9 |
| 10 |
| 100 |
| 200 |
| 300 |
| 999 |
+-----+
此外,为了演示外部处理程序是否可以处理来自3个级别的SQLEXCEPTION,我特意犯了n2.1
个错误.然后让我们看看主nn
中的出口处理程序是否会处理它.
delimiter //
drop procedure if exists n1 //
drop procedure if exists n2 //
drop procedure if exists `n2.1` //
drop procedure if exists nn //
create procedure n1()
begin
declare n int default 1;
while n<=5 do
insert test values(n);
set n=n+1;
end while;
end//
create procedure `n2.1`()
begin
insert test values(999);
insert test values(999);
end//
create procedure n2()
begin
declare n int default 6;
call `n2.1`;
while n<=10 do
insert test values(n);
set n=n+1;
end while;
end//
create procedure nn()
begin
declare exit handler for sqlexception
begin
commit;
resignal;
end;
truncate test;
start transaction ;
insert test values(100);
call n1;
insert test values(200);
call n2;
insert test values(300);
commit;
end//
delimiter ;
现在是关键时刻了.
call nn;
ERROR 1062 (23000): Duplicate entry '999' for key 'PRIMARY'
select * from test;
+-----+
| id |
+-----+
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
| 100 |
| 200 |
| 999 |
+-----+
如结果所示,nn
中的退出处理程序通过提交到目前为止的更改并在终止过程之前发出错误消息来处理n2.1
中的错误.
现在,如果您想知道如果n2
和nn
都有处理程序会发生什么情况.我还为你准备了另外两个箱子.在情况1中,n2
通过删除其处理程序中的resignal
来静音错误,而在情况2中,n2
通过在其处理程序中删除resignal
来通知错误.请注意,在这两种情况下,这次都使用了n2
个Continue处理程序.
delimiter //
drop procedure if exists n1 //
drop procedure if exists n2 //
drop procedure if exists `n2.1` //
drop procedure if exists nn //
create procedure n1()
begin
declare n int default 1;
while n<=5 do
insert test values(n);
set n=n+1;
end while;
end//
create procedure `n2.1`()
begin
insert test values(999);
insert test values(999);
end//
create procedure n2()
begin
declare n int default 6;
declare continue handler for sqlexception
begin
rollback;
end;
call `n2.1`;
while n<=10 do
insert test values(n);
set n=n+1;
end while;
end//
create procedure nn()
begin
declare exit handler for sqlexception
begin
commit;
resignal;
end;
truncate test;
start transaction ;
insert test values(100);
call n1;
insert test values(200);
call n2;
insert test values(300);
commit;
end//
delimiter ;
以下是第一种情况的结果:
call nn;
Query OK, 0 rows affected (0.03 sec)
select * from test;
+-----+
| id |
+-----+
| 6 |
| 7 |
| 8 |
| 9 |
| 10 |
| 300 |
+-----+
正如结果所反映的那样,到目前为止,n2
个回滚的更改并继续进行(没有因为缺少resignal
而引发错误).而nn
人则继续工作,就像到目前为止没有任何异常发生一样.
现在如果你还在听我的话,第二种情况就来了.注意处理程序的内容,因为n2
有提交和重发信号,而nn
有回滚(为了通知而重发信号).
delimiter //
drop procedure if exists n1 //
drop procedure if exists n2 //
drop procedure if exists `n2.1` //
drop procedure if exists nn //
create procedure n1()
begin
declare n int default 1;
while n<=5 do
insert test values(n);
set n=n+1;
end while;
end//
create procedure `n2.1`()
begin
insert test values(999);
insert test values(999);
end//
create procedure n2()
begin
declare n int default 6;
declare continue handler for sqlexception
begin
commit;
resignal;
end;
call `n2.1`;
while n<=10 do
insert test values(n);
set n=n+1;
end while;
end//
create procedure nn()
begin
declare exit handler for sqlexception
begin
rollback;
resignal;
end;
truncate test;
start transaction ;
insert test values(100);
call n1;
insert test values(200);
call n2;
insert test values(300);
commit;
end//
delimiter ;
现在,我们在 case 2中得到了真相.
call nn;
ERROR 1062 (23000): Duplicate entry '999' for key 'PRIMARY'
select * from test;
+-----+
| id |
+-----+
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
| 100 |
| 200 |
| 999 |
+-----+
因此,n2
提交了到目前为止的更改,同时也喊出了一个错误.HERE IS A BOOKMARK WHICH WE WILL COME BACK SHORTLY AFTER.收到错误后,nn
执行回滚,当然无法undo撤消n2
已提交的更改,并取消其余更改.
现在让我们回到书签上.当n2
喊出错误时,你认为真正发生了什么?因为对于它的Continue处理程序,由于处理程序操作为continue
,它应该继续其工作.但是来自nn
的处理程序只会回滚并终止当前过程.判断下面的代码以找出答案.注意,在n2
的末尾有一个提交,以便对真实发生的事情进行证明.
delimiter //
drop procedure if exists n1 //
drop procedure if exists n2 //
drop procedure if exists `n2.1` //
drop procedure if exists nn //
create procedure n1()
begin
declare n int default 1;
while n<=5 do
insert test values(n);
set n=n+1;
end while;
end//
create procedure `n2.1`()
begin
insert test values(999);
insert test values(999);
end//
create procedure n2()
begin
declare n int default 6;
declare continue handler for sqlexception
begin
commit;
resignal;
end;
call `n2.1`;
while n<=10 do
insert test values(n);
set n=n+1;
end while;
commit;
end//
create procedure nn()
begin
declare exit handler for sqlexception
begin
rollback;
resignal;
end;
truncate test;
start transaction ;
insert test values(100);
call n1;
insert test values(200);
call n2;
insert test values(300);
commit;
end//
delimiter ;
看哪,窗帘在这里揭开了面纱.
call nn;
ERROR 1062 (23000): Duplicate entry '999' for key 'PRIMARY'
select * from test;
+-----+
| id |
+-----+
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
| 100 |
| 200 |
| 999 |
+-----+
它以n2
结束,没有使用处理程序动作continue
完成其过程的其余部分.这意味着,主过程nn
中的退出处理程序一旦接收到来自n2
的错误,就废除一切,而不是在执行其回滚和重发信号职责之前.
这是一本很长的书,我希望你没有太厌烦.希望这能有所帮助.