13.6.7.2 DECLARE ... HANDLER 语句

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
}

声明...处理程序语句指定处理一个或多个条件的处理程序。如果出现这些情况之一,则执行指定的* statement *。 * statement *可以是简单的语句,例如SET var_name = value,也可以是使用BEGINEND编写的复合语句(请参见第 13.6.1 节“ BEGIN ... END 复合语句”)。

处理程序声明必须出现在变量或条件声明之后。

  • handler_action *值指示在执行处理程序语句后处理程序将执行什么操作:
  • CONTINUE:continue 执行当前程序。

  • EXIT:对于声明处理程序的开始...结束复合语句,执行终止。即使条件发生在内部块中,也是如此。

  • UNDO:不支持。

声明...处理程序的* condition_value *table 示激活处理程序的特定条件或条件类别。它可以采用以下形式:

    • mysql_error_code *:指示 MySQL 错误代码的整数 Literals,例如 1051,用于指定“未知 table”:
DECLARE CONTINUE HANDLER FOR 1051
  BEGIN
    -- body of handler
  END;

不要使用 MySQL 错误代码 0,因为这 table 示成功而不是错误情况。有关 MySQL 错误代码的列 table,请参见第 B.3.1 节“服务器错误消息参考”

  • SQLSTATE [VALUE] * sqlstate_value *:5 个字符的字符串 Literals,指示 SQLSTATE 值,例如'42S01',用于指定“未知 table”:
DECLARE CONTINUE HANDLER FOR SQLSTATE '42S02'
  BEGIN
    -- body of handler
  END;

不要使用以'00'开头的 SQLSTATE 值,因为这些值 table 示成功而不是错误情况。有关 SQLSTATE 值的列 table,请参见第 B.3.1 节“服务器错误消息参考”

DECLARE CONTINUE HANDLER FOR SQLWARNING
  BEGIN
    -- body of handler
  END;
  • NOT FOUND:以'02'开头的 SQLSTATE 值类的简写。这在游标的上下文中是相关的,用于控制游标到达数据集末尾时发生的情况。如果没有更多行可用,则使用 SQLSTATE 值'02000'发生 No Data 条件。要检测此条件,可以为其设置处理程序或NOT FOUND条件。
DECLARE CONTINUE HANDLER FOR NOT FOUND
  BEGIN
    -- body of handler
  END;

有关另一个示例,请参见第 13.6.6 节“游标”。对于不检索任何行的SELECT ... INTO var_list语句,也会发生NOT FOUND条件。

  • SQLEXCEPTION:不是以'00''01''02'开头的 SQLSTATE 值类别的简写。
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
  BEGIN
    -- body of handler
  END;

有关在条件发生时服务器如何选择处理程序的信息,请参见第 13.6.7.6 节“处理程序的范围规则”

如果发生未声明任何处理程序的条件,则采取的操作取决于条件类:

  • 对于SQLEXCEPTION条件,存储程序终止于引发该条件的语句,就像存在EXIT处理程序一样。如果该程序被另一个存储程序调用,则调用程序使用应用于其自己的处理程序的处理程序选择规则来处理该条件。

  • 对于SQLWARNING条件,程序将 continue 执行,就像有CONTINUE处理程序一样。

  • 对于NOT FOUND条件,如果条件正常升高,则动作为CONTINUE。如果它是由SIGNALRESIGNAL引发的,则动作为EXIT

以下示例对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)

请注意,过程执行后@x3,这 table 明在错误发生之后,执行将 continue 到过程结束。如果不存在声明...处理程序语句,则在第二个INSERT由于PRIMARY KEY约束而失败后,MySQL 将采取默认操作(EXIT),并且SELECT @x将返回2

要忽略条件,请为其声明CONTINUE处理程序并将其与空块关联。例如:

DECLARE CONTINUE HANDLER FOR SQLWARNING BEGIN END;

块标签的范围不包括在块内声明的处理程序的代码。因此,与处理程序关联的语句不能使用ITERATELEAVE来引用包含处理程序声明的块的标签。考虑下面的示例,其中REPEAT块的标签为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处理程序。如果不需要块清除,则开始...结束处理程序主体可以为空:
DECLARE EXIT HANDLER FOR SQLWARNING BEGIN END;

否则,将清除语句放入处理程序主体中:

DECLARE EXIT HANDLER FOR SQLWARNING
  BEGIN
    block cleanup statements
  END;
  • 要 continue 执行,请在CONTINUE处理程序中设置状态变量,可以在封闭的块中检查该状态变量以确定是否调用了该处理程序。下面的示例为此使用变量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;