存储程序可能包含处理程序,以便在程序中出现某些条件时被调用。每个处理程序的适用性取决于其在程序定义中的位置和它处理的条件:
-
在
BEGIN ... END
块中声明的处理程序仅在该块中的 SQL 语句后生效。如果处理程序本身引发了条件,它不能处理该条件,也不能处理块中的其他处理程序。在以下示例中,处理程序H1
和H2
适用于语句stmt1
和stmt2
所引发的条件。但是, neitherH1
norH2
适用于处理程序H1
或H2
的主体中的条件。BEGIN -- outer block DECLARE EXIT HANDLER FOR ...; -- handler H1 DECLARE EXIT HANDLER FOR ...; -- handler H2 stmt1; stmt2; END;
-
处理程序仅在其声明的块中生效,不能激活块外部的条件。在以下示例中,处理程序
H1
适用于内层块中的stmt1
,但不适用于外层块中的stmt2
。BEGIN -- outer block BEGIN -- inner block DECLARE EXIT HANDLER FOR ...; -- handler H1 stmt1; END; stmt2; END;
-
处理程序可以是特定的或一般的。特定的处理程序是为 MySQL 错误代码、
SQLSTATE
值或条件名称。一般的处理程序是为SQLWARNING
、SQLEXCEPTION
或NOT FOUND
类的条件。条件的特异性与条件优先级相关,如后面所述。
可以在不同的作用域中声明多个处理程序,每个处理程序具有不同的特异性。例如,可能在外层块中有一个特定的 MySQL 错误代码处理程序,而在内层块中有一个一般的 SQLWARNING
处理程序。或者,在同一个块中可能有一个特定的 MySQL 错误代码处理程序和一个一般的 SQLWARNING
处理程序。
处理程序是否被激活不仅取决于其自己的作用域和条件值,还取决于其他处理程序的存在。当条件在存储程序中出现时,服务器将在当前作用域(当前 BEGIN ... END
块)中搜索适用的处理程序。如果没有找到适用的处理程序,搜索将继续向外扩展到包含的作用域(块)。当服务器在给定的作用域中找到一个或多个适用的处理程序时,它将根据条件优先级选择其中一个:
-
MySQL 错误代码处理程序优先于
SQLSTATE
值处理程序。 -
一个
SQLSTATE
值处理程序优先于一般的SQLWARNING
、SQLEXCEPTION
或NOT FOUND
处理程序。 -
一个
SQLEXCEPTION
处理程序优先于一个SQLWARNING
处理程序。 -
可能存在多个适用的处理程序具有相同的优先级。例如,一个语句可能生成多个警告,每个警告都有一个特定的错误代码处理程序。在这种情况下,服务器选择哪个处理程序的激活是非确定性的,可能会根据条件出现的环境而变化。
处理程序选择规则的一个推论是,如果多个适用的处理程序出现在不同的作用域中,那么具有最 local 作用域的处理程序优先于外层作用域中的处理程序,甚至优先于那些具有更具体条件的处理程序。
如果在条件出现时没有适用的处理程序,采取的操作取决于条件的类别:
以下示例演示了 MySQL 如何应用处理程序选择规则。
该过程包含两个处理程序,一个是特定的 SQLSTATE
值 ('42S02'
),该值在尝试删除不存在的表时出现,另一个是 general SQLEXCEPTION
类:
CREATE PROCEDURE p1()
BEGIN
DECLARE CONTINUE HANDLER FOR SQLSTATE '42S02'
SELECT 'SQLSTATE handler was activated' AS msg;
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
SELECT 'SQLEXCEPTION handler was activated' AS msg;
DROP TABLE test.t;
END;
这两个处理程序在同一个块中声明,并且具有相同的作用域。然而,SQLSTATE
处理程序优先于 SQLEXCEPTION
处理程序,因此如果表 t
不存在,则 DROP TABLE
语句将引发一个激活 SQLSTATE
处理程序的条件:
mysql> CALL p1();
+--------------------------------+
| msg |
+--------------------------------+
| SQLSTATE handler was activated |
+--------------------------------+
该过程包含相同的两个处理程序。但这次,DROP TABLE
语句和 SQLEXCEPTION
处理程序位于 SQLSTATE
处理程序的内部块中:
CREATE PROCEDURE p2()
BEGIN -- outer block
DECLARE CONTINUE HANDLER FOR SQLSTATE '42S02'
SELECT 'SQLSTATE handler was activated' AS msg;
BEGIN -- inner block
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
SELECT 'SQLEXCEPTION handler was activated' AS msg;
DROP TABLE test.t; -- occurs within inner block
END;
END;
在这种情况下,发生条件的位置最近的处理程序优先。 SQLEXCEPTION
处理程序激活,即使它比 SQLSTATE
处理程序更 general:
mysql> CALL p2();
+------------------------------------+
| msg |
+------------------------------------+
| SQLEXCEPTION handler was activated |
+------------------------------------+
在这个过程中,一个处理程序在 DROP TABLE
语句的作用域内声明:
CREATE PROCEDURE p3()
BEGIN -- outer block
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
SELECT 'SQLEXCEPTION handler was activated' AS msg;
BEGIN -- inner block
DECLARE CONTINUE HANDLER FOR SQLSTATE '42S02'
SELECT 'SQLSTATE handler was activated' AS msg;
END;
DROP TABLE test.t; -- occurs within outer block
END;
只有 SQLEXCEPTION
处理程序适用,因为其他处理程序不在 DROP TABLE
语句引发的条件的作用域内:
mysql> CALL p3();
+------------------------------------+
| msg |
+------------------------------------+
| SQLEXCEPTION handler was activated |
+------------------------------------+
在这个过程中,两个处理程序都在 DROP TABLE
语句的作用域内声明:
CREATE PROCEDURE p4()
BEGIN -- outer block
BEGIN -- inner block
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
SELECT 'SQLEXCEPTION handler was activated' AS msg;
DECLARE CONTINUE HANDLER FOR SQLSTATE '42S02'
SELECT 'SQLSTATE handler was activated' AS msg;
END;
DROP TABLE test.t; -- occurs within outer block
END;
两个处理程序都不适用,因为它们不在 DROP TABLE
语句引发的条件的作用域内。该语句引发的条件未被处理,并以错误终止过程:
mysql> CALL p4();
ERROR 1051 (42S02): Unknown table 'test.t'