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