PL/SQLでプログラムを実行する際、予期しないエラーが発生することがあります。このようなエラーを適切に処理するためには、例外処理を実装することが重要です。この記事では、PL/SQLにおける例外処理の基本から独自の例外の定義、エラーハンドリングの実践例までを解説します。
PL/SQLにおける例外処理の基本構造
PL/SQLでは、例外が発生するとそれを処理するためのEXCEPTIONブロックが用意されています。通常の処理とエラーハンドリングを区別するため、以下のような基本構造になります。
BEGIN
-- 通常の処理
EXCEPTION
WHEN 例外名 THEN
-- エラー時の処理
END;
BEGINからEXCEPTIONまでが通常の処理、EXCEPTIONからENDまでが例外が発生した場合の処理を記述する部分です。
事前定義された例外
PL/SQLでは、よくあるエラーに対して事前に定義された例外がいくつか用意されています。これらの例外は明示的に定義せずとも使用できます。代表的な事前定義された例外には以下があります。
- NO_DATA_FOUND: SELECT文でデータが見つからなかった場合に発生
- TOO_MANY_ROWS: SELECT文が複数行を返した場合に発生
- ZERO_DIVIDE: 0による除算が行われた場合に発生
- INVALID_CURSOR: 無効なカーソル操作が行われた場合に発生
例として、0による除算の例外処理を見てみましょう。
BEGIN
v_result := 10 / v_divisor;
EXCEPTION
WHEN ZERO_DIVIDE THEN
DBMS_OUTPUT.PUT_LINE('ゼロで割ろうとしました。');
END;
独自の例外を定義する方法
PL/SQLでは、独自の例外を定義してプログラム中で任意のタイミングで例外を発生させることができます。独自の例外はDECLAREブロックで宣言し、RAISEを使用して明示的に例外を発生させます。
DECLARE
e_custom_exception EXCEPTION;
BEGIN
IF condition THEN
RAISE e_custom_exception;
END IF;
EXCEPTION
WHEN e_custom_exception THEN
DBMS_OUTPUT.PUT_LINE('独自例外が発生しました。');
END;
このようにして、独自のロジックに基づいて例外を発生させ、特定のエラーハンドリングを行うことが可能です。
OTHERSを使用した汎用的なエラーハンドリング
OTHERSは、すべての例外をキャッチする汎用的なハンドラです。事前定義された例外や独自の例外をキャッチした後、それ以外のエラーに対しても対応したい場合に使用します。
BEGIN
-- 通常の処理
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('データが見つかりませんでした。');
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('その他のエラーが発生しました。');
END;
OTHERSは、予期しないエラーが発生した場合に安全に処理を行うための最後の砦として機能します。
例外発生後のロールバック処理
例外が発生した場合、データベースで行われた変更は自動的にロールバックされません。トランザクション処理を伴う場合は、例外が発生した際に手動でROLLBACKやCOMMITを行う必要があります。
BEGIN
UPDATE employees SET salary = salary * 1.1 WHERE department_id = 10;
COMMIT;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
DBMS_OUTPUT.PUT_LINE('トランザクションが失敗しました。');
END;
このようにして、トランザクションが中途半端な状態で残らないようにすることが大切です。
明示的な例外処理とログ記録の例
DECLARE
v_emp_name employees.last_name%TYPE;
BEGIN
SELECT last_name INTO v_emp_name
FROM employees
WHERE employee_id = 9999; -- 存在しないID
DBMS_OUTPUT.PUT_LINE('社員名: ' || v_emp_name);
EXCEPTION
WHEN NO_DATA_FOUND THEN
INSERT INTO error_log (error_time, error_msg)
VALUES (SYSDATE, '社員が見つかりませんでした');
COMMIT;
DBMS_OUTPUT.PUT_LINE('該当する社員が存在しません');
END;
存在しない社員IDを指定して例外を発生させ、そのエラーをログテーブルに記録します。実務ではこのように、ログ保存を行う構成が重要です。
複数例外の処理(RAISE_APPLICATION_ERROR の活用)
DECLARE
v_salary employees.salary%TYPE;
BEGIN
SELECT salary INTO v_salary
FROM employees
WHERE employee_id = 100;
IF v_salary < 0 THEN
RAISE_APPLICATION_ERROR(-20001, '給与が負の値になっています');
END IF;
DBMS_OUTPUT.PUT_LINE('給与: ' || v_salary);
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('社員が見つかりません');
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('その他のエラー: ' || SQLERRM);
END;
業務ロジック上のエラー(給与が負)に対して、明示的に RAISE_APPLICATION_ERROR を使って例外を発生させています。
トランザクションとの連携
BEGIN
UPDATE employees
SET salary = salary * 1.1
WHERE department_id = 10;
-- 故意にエラーを発生
INSERT INTO non_existing_table VALUES ('test');
COMMIT;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
DBMS_OUTPUT.PUT_LINE('エラー発生によりロールバックしました: ' || SQLERRM);
END;
例外発生時に ROLLBACK を行うことで、データの一貫性を保つ処理の例です。
例外処理のベストプラクティス
PL/SQLで例外処理を記述する際は、単にエラーをキャッチして終了させるだけでは不十分です。実運用を想定した堅牢なコードにするには、以下のようなベストプラクティスを意識することが重要です。
不要な WHEN OTHERS の乱用を避ける
PL/SQLには WHEN OTHERS
という汎用例外キャッチ構文がありますが、これをすべての例外処理に使ってしまうと、意図しない不具合の原因を特定できなくなります。特定の例外(NO_DATA_FOUND
や TOO_MANY_ROWS
など)は明示的に処理し、WHEN OTHERS
は最後の安全ネットとして限定的に使用するのが望ましいです。
例外メッセージはログに記録する
例外が発生した際には、SQLERRM
や SQLCODE
などのシステム変数を活用して、発生したエラーの内容をログテーブルに記録することで、後続のデバッグや監査に役立ちます。
EXCEPTION
WHEN OTHERS THEN
INSERT INTO error_log (error_time, error_code, error_msg)
VALUES (SYSDATE, SQLCODE, SQLERRM);
ROLLBACK;
DBMS_OUTPUT.PUT_LINE('エラーが発生しました。詳細はログを参照してください。');
リソースの解放を忘れずに
カーソルやファイルなど、明示的に開いたリソースは、例外発生時にも確実にクローズされるようにしておく必要があります。カーソルであれば CLOSE
文を EXCEPTION
節にも記述しましょう。
業務ロジックのエラーは明示的に例外として処理
「0件ヒットした」「無効な金額が入力された」など、SQL自体は正常でも業務的には異常な場合は、RAISE_APPLICATION_ERROR
を使って明示的に例外として扱い、エラーとして認識させると保守性が向上します。
IF v_amount < 0 THEN
RAISE_APPLICATION_ERROR(-20002, '金額が不正です(マイナス値)');
END IF;
処理の中断と復帰を明確に設計
例外処理の中で「何をもって処理を中断し」「どのように復旧させるか」のポリシーを明確にしておくと、コード全体の可読性・メンテナンス性が高まります。特に複数の例外ポイントがある場合は、処理フローの整理が鍵となります。
このようなベストプラクティスを意識することで、PL/SQLコードの堅牢性と信頼性が大きく向上し、将来的なトラブルを未然に防ぐことができます。
まとめ
PL/SQLの例外処理は、プログラムの信頼性を高めるための重要な要素です。事前定義された例外に加え、独自の例外を定義することで、より柔軟で細かいエラーハンドリングが可能になります。例外が発生した場合の適切な処理を実装することで、堅牢なPL/SQLプログラムを作成しましょう。