存储程序可以包含处理程序,这些处理程序在程序中发生某些条件时会被调用。每个处理程序的适用性取决于它在程序定义中的位置以及它处理的条件。
在
BEGIN ... END
块中声明的处理程序仅对块中处理程序声明之后的 SQL 语句有效。如果处理程序本身引发了条件,它不能处理该条件,块中声明的其他处理程序也不能处理该条件。在下面的示例中,处理程序H1
和H2
对语句stmt1
和stmt2
引发的条件有效。但H1
和H2
都不对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
处理程序。可以存在多个具有相同优先级的适用处理程序。例如,语句可能会生成具有不同错误代码的多个警告,每个警告都存在一个错误特定的处理程序。在这种情况下,服务器激活哪个处理程序的选择是不确定的,并且可能会根据条件发生的具体情况而改变。
处理程序选择规则的一个含义是,如果在不同作用域中存在多个适用处理程序,则具有最局部作用域的处理程序优先于外层作用域的处理程序,即使是针对更特定条件的处理程序。
如果条件发生时没有合适的处理程序,采取的行动将取决于条件的类别。
以下示例演示了 MySQL 如何应用处理程序选择规则。
此过程包含两个处理程序,一个用于尝试删除不存在的表时发生的特定 SQLSTATE
值 ('42S02'
),另一个用于通用的 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
处理程序更通用。
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'