存储程序可以包含在程序中发生某些条件时要调用的处理程序。每个处理程序的适用性取决于它在程序定义中的位置以及它处理的条件。
在
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'