DECLARE ... HANDLER ステートメントは、1つまたは複数の例外条件を処理するハンドラーを指定するために使用されます。特定の条件が発生した場合、指定されたステートメントが実行されます。
構文とパラメータの説明
ハンドラー宣言は、変数または条件宣言の後に記述する必要があります。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 は、単純なステートメント(例:SET var_name = 1)の場合もあれば、BEGIN ... END ブロックで記述された複合ステートメントの場合もあります。詳細については、PL構文の構造を参照してください。
handler_action は、ハンドラーステートメントの実行後にハンドラーが実行する操作を指示します。詳細は以下のとおりです:
CONTINUE:現在のプログラムの実行を続けます。EXIT:ハンドラーのBEGIN ... ENDブロック内のステートメントの実行が完了した後、このストアドプロシージャの実行を終了します。UNDO:サポートされていません。
condition_value は、ハンドラーを有効化する特定の条件または条件カテゴリを指示します。以下の形式を取ることができます:
mysql_error_code:エラーコードを表す整数リテラル。例えば、5217 は "unknown column" を表します。SQLWARNING:'01' で始まるSQLSTATE値の略称です。condition_name:以前にDECLARE ... CONDITIONで指定した条件名です。条件名はエラーコードまたはSQLSTATE値に関連付けることができます。NOT FOUND:'02' で始まるSQLSTATE値の略称です。これはカーソルのコンテキストに依存し、カーソルがデータセットの末尾に到達したときに何が起こるかを制御するために使用されます。利用可能な行がない場合、SQLSTATE値が "02000" の No Data 条件が発生します。この状況を検出するには、処理手順を設定するか、NOT FOUND条件に対する処理手順を設定することができます。SQLEXCEPTION:"00"、"01"、または "02" で始まらないSQLSTATE値の略称です。
ハンドラーが宣言されていない条件が発生した場合、条件のカテゴリに応じて以下の操作が実行されます:
SQLEXCEPTION条件の場合、ストアドプロシージャは条件を引き起こしたステートメントで終了します。これは、EXITハンドラーを使用した場合と同様です。そのプログラムが別のストアドプロシージャによって呼び出されている場合、呼び出し元のプログラムが自身の選択ルールに従ってその条件を処理します。SQLWARNING条件の場合、プログラムはCONTINUEハンドラーを使用した場合と同様に実行を続けます。NOT FOUND条件の場合、条件が正常に発生した場合の動作はCONTINUEです。SIGNALまたはRESIGNALによって発生した場合、動作はEXITです。
以下の例では、DECLARE ... HANDLER ステートメントを使用して、重複キーエラーが発生した場合に CONTINUE 操作を実行するよう宣言しています。そのため、PRIMARY KEY の制約があっても、2回目の INSERT が失敗した後、デフォルトの EXIT 操作は実行されず、最終的な実行結果は 3 となります。
obclient> CREATE TABLE test.tbl1 (c1 INT, PRIMARY KEY (c1));
Query OK, 0 rows affected
obclient> delimiter //
obclient> CREATE PROCEDURE handler_demo()
BEGIN
DECLARE CONTINUE HANDLER FOR SQLSTATE '23000' SET @x = 1;
INSERT INTO test.tbl1 VALUES (101);
SET @x = 2;
INSERT INTO test.tbl1 VALUES (101);
SET @x = 3;
END;
//
Query OK, 0 rows affected
obclient> delimiter ;
obclient> CALL handler_demo();
Query OK, 0 rows affected
obclient> SELECT @x;
+------+
| @x |
+------+
| 3 |
+------+
1 row in set
特定の条件を無視する必要がある場合、その条件に対して CONTINUE ハンドラーを宣言し、空のブロックと関連付けることができます。例:
DECLARE CONTINUE HANDLER FOR SQLWARNING BEGIN END;
ブロックラベルの使用制限
ブロックラベルの範囲には、そのブロック内で宣言されたハンドラーのコードは含まれません。したがって、ハンドラーに関連付けられたステートメントでは、ITERATE または LEAVE を使用して、ハンドラー宣言を含むブロックラベルを参照することはできません。
以下の例では、REPEAT ブロックに retry_lable ラベルがありますが、この retry_lable ラベルはブロック内の IF ステートメントの範囲内にはあるものの、CONTINUE ハンドラーの範囲内にはありません。そのため、REPEAT ブロックが retry_lable ラベルを参照することは無効であり、エラーを引き起こします。
obclient> CREATE PROCEDURE proc1 ()
BEGIN
DECLARE n INT DEFAULT 10;
retry_lable:
REPEAT
BEGIN
DECLARE CONTINUE HANDLER FOR SQLWARNING
BEGIN
ITERATE retry_lable; # 無効なラベル
END;
IF n < 0 THEN
LEAVE retry_lable; # 有効なラベル
END IF;
SET n = n - 1;
END;
UNTIL FALSE END REPEAT;
END //
ERROR 1308 (42000): no matching label: retry_lable
ハンドラー内で外部ラベルを参照することを避けるには、以下の戦略を使用できます:
EXITハンドラーを使用してそのブロックを終了し、クリーンアップステートメントをハンドラー本体に配置します。ブロックのクリーンアップが不要な場合、BEGIN ... ENDで囲まれたハンドラー本体は空にすることができます。例:DECLARE EXIT HANDLER FOR SQLWARNING BEGIN block cleanup statements # ブロックのクリーンアップステートメント END;実行を続行するには、
CONTINUEハンドラー内で状態変数を設定し、閉じたブロック内でその状態変数をチェックすることで、ハンドラーが呼び出されたかどうかを判断します。以下の例では、変数doneを使用してこの目的を実現しています。obclient> CREATE PROCEDURE proc2() BEGIN DECLARE n INT DEFAULT 10; DECLARE done INT DEFAULT FALSE; retry_lable: REPEAT BEGIN DECLARE CONTINUE HANDLER FOR SQLWARNING BEGIN SET done = TRUE; END; IF done OR n < 0 THEN LEAVE retry_lable; END IF; SET n = n - 2; END; UNTIL FALSE END REPEAT; END;// Query OK, 0 rows affected