15.6.7.2 声明处理程序语句
DECLARE handler_action HANDLER
FOR condition_value [, condition_value] ...
statement
handler_action: {
CONTINUE
| EXIT
| UNDO
}
condition_value: {
mysql_error_code
| SQLSTATE [VALUE] sqlstate_value
| condition_name
| SQLWARNING
| NOT FOUND
| SQLEXCEPTION
}
DECLARE ... HANDLER
语句指定一个或多个条件的处理程序。如果这些条件之一发生,指定的 statement
执行。 statement
可以是一个简单语句,如 SET
,或使用 var_name
= value
BEGIN
和 END
编写的复合语句(见第15.6.1节,“BEGIN ... END 复合语句”)。
处理程序声明必须在变量或条件声明后出现。
handler_action
值指示处理程序执行完毕后的操作:
-
CONTINUE
:当前程序的执行继续。 -
EXIT
:在声明了处理程序的BEGIN ... END
复合语句中,执行终止。这也适用于条件发生在内层块中。 -
UNDO
:不支持。
在DECLARE ... HANDLER
中,condition_value
指定激活处理程序的特定条件或条件类别。它可以以以下形式出现:
-
mysql_error_code
:一个整数字面量,表示 MySQL 错误代码,例如指定 “未知表”:DECLARE CONTINUE HANDLER FOR 1051 BEGIN -- body of handler END;
不要使用 MySQL 错误代码 0,因为那表示成功,而不是错误条件。关于 MySQL 错误代码的列表,请参见服务器错误消息参考。
-
SQLSTATE [VALUE]
sqlstate_value
:一个 5 个字符字面量,表示 SQLSTATE 值,例如指定'42S01'
,表示 “未知表”:DECLARE CONTINUE HANDLER FOR SQLSTATE '42S02' BEGIN -- body of handler END;
不要使用以
'00'
开头的 SQLSTATE 值,因为那表示成功,而不是错误条件。关于 SQLSTATE 值的列表,请参见服务器错误消息参考。 -
condition_name
:之前使用DECLARE ... CONDITION
指定的条件名称。条件名称可以与 MySQL 错误代码或 SQLSTATE 值相关联。请参见第15.6.7.1节,“DECLARE ... CONDITION 语句”。 -
SQLWARNING
:简写 SQLSTATE 值的类别,值以'01'
开头。DECLARE CONTINUE HANDLER FOR SQLWARNING BEGIN -- body of handler END;
-
NOT FOUND
:简写 SQLSTATE 值的类别,值以'02'
开头。在游标上下文中,这个值用来控制游标到达数据集末尾时的行为。如果没有更多行可用,会出现“无数据”情况,SQLSTATE 值为'02000'
。要检测这个情况,可以设置该值或NOT FOUND
情况的处理程序。DECLARE CONTINUE HANDLER FOR NOT FOUND BEGIN -- body of handler END;
另一个示例,请见第15.6.6节,“游标”。
NOT FOUND
情况也会出现于SELECT ... INTO
语句中,如果没有检索到行。var_list
-
SQLEXCEPTION
:简写 SQLSTATE 值的类别,值不以'00'
、'01'
或'02'
开头。DECLARE CONTINUE HANDLER FOR SQLEXCEPTION BEGIN -- body of handler END;
关于服务器在条件出现时选择处理程序的信息,请见第15.6.7.6节,“处理程序作用域规则”。
如果出现未声明的条件,行为取决于条件类别:
以下示例使用了一个处理器来捕捉SQLSTATE '23000'
,该状态码用于重复键错误:
mysql> CREATE TABLE test.t (s1 INT, PRIMARY KEY (s1));
Query OK, 0 rows affected (0.00 sec)
mysql> delimiter //
mysql> CREATE PROCEDURE handlerdemo ()
BEGIN
DECLARE CONTINUE HANDLER FOR SQLSTATE '23000' SET @x2 = 1;
SET @x = 1;
INSERT INTO test.t VALUES (1);
SET @x = 2;
INSERT INTO test.t VALUES (1);
SET @x = 3;
END;
//
Query OK, 0 rows affected (0.00 sec)
mysql> CALL handlerdemo()//
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT @x//
+------+
| @x |
+------+
| 3 |
+------+
1 row in set (0.00 sec)
注意,@x
在程序执行完毕后是3
,显示了在错误发生后程序继续执行到程序结尾。如果没有DECLARE ... HANDLER
语句,MySQL 将在第二个INSERT
失败时由于PRIMARY KEY
约束而退出,并且SELECT @x
将返回2
。
要忽略一个条件,声明一个CONTINUE
处理器并将其与空块关联。例如:
DECLARE CONTINUE HANDLER FOR SQLWARNING BEGIN END;
块标签的作用域不包括在块中声明的处理程序代码。因此,关联处理程序语句不能使用ITERATE
或LEAVE
来引用块中的标签。考虑以下示例,where theREPEAT
块的标签为retry
:
CREATE PROCEDURE p ()
BEGIN
DECLARE i INT DEFAULT 3;
retry:
REPEAT
BEGIN
DECLARE CONTINUE HANDLER FOR SQLWARNING
BEGIN
ITERATE retry; # illegal
END;
IF i < 0 THEN
LEAVE retry; # legal
END IF;
SET i = i - 1;
END;
UNTIL FALSE END REPEAT;
END;
在块中,retry
标签对IF
语句可用,但对CONTINUE
处理程序不可用,结果报错:
ERROR 1308 (42000): LEAVE with no matching label: retry
避免在处理程序中引用外部标签,可以使用以下策略:
-
要离开块,使用
EXIT
处理程序。如果不需要块清理语句,BEGIN ... END
处理程序体可以为空:DECLARE EXIT HANDLER FOR SQLWARNING BEGIN END;
否则,在处理程序体中放置清理语句:
DECLARE EXIT HANDLER FOR SQLWARNING BEGIN block cleanup statements END;
-
要继续执行,设置在封闭块中的状态变量,以确定是否调用了处理程序。以下示例使用变量
done
来实现这个目的:CREATE PROCEDURE p () BEGIN DECLARE i INT DEFAULT 3; DECLARE done INT DEFAULT FALSE; retry: REPEAT BEGIN DECLARE CONTINUE HANDLER FOR SQLWARNING BEGIN SET done = TRUE; END; IF done OR i < 0 THEN LEAVE retry; END IF; SET i = i - 1; END; UNTIL FALSE END REPEAT; END;