5-2.その他の例外処理~PRAGMAを使用した例外処理~

PL/SQLの例外制御方法は前に述べた方法だけではありません。

他の例外を制御する方法としては、ユーザー定義例外にPRAGMA EXCEPTION_INIT()を使用する方法や、RAISE_APPLICATION_ERRORを使用する方法があります。

参考URL:
PL/SQL 言語リファレンス PL/SQLのエラー処理
https://docs.oracle.com/cd/E16338_01/appdev.112/b56260/errors.htm

PRAGMA EXCEPTION_INIT()は、既存のシステム定義例外をエラー番号指定で発生させたい場合に使用します。

ただ、最初に言ってしまいますが、一般的な(特に大規模な)業務システムにおいてこの方法はオススメできません。
後述しますが、多数の例外パターンの管理ができませんので、あくまでも「5-1.例外処理の基本(https://oracle.tf17.net/plsql/?page_id=292)」で記載した方法が例外処理の基本になると思って頂いて良いと思います。

PRAGMA EXCEPTION_INITの使用方法

 ユーザー定義例外を宣言した後、その例外に対し、PRAGMA EXCEPTION_INITを宣言します。
 前回の例外処理用のサンプルコードを元に、PRAGMA EXCEPTION_INITを使用した例を作成してみました。

CREATE OR REPLACE PACKAGE BODY expt_sample
IS
  expt_sal_over_range EXCEPTION;  --ユーザー定義例外
  PRAGMA EXCEPTION_INIT(expt_sal_over_range, -6502);  -- ここでユーザー定義例外に割り当て
--  PRAGMA EXCEPTION_INIT(expt_sal_over_range, -20001);
--
  CURSOR c_emp(iv_dept IN VARCHAR2)
  IS
  SELECT e.empno, e.ename, e.deptno, d.dname, e.sal*100 AS sal, e.comm
  FROM   emp e INNER JOIN dept d ON (e.deptno = d.deptno)
  WHERE  e.deptno = iv_dept;
--
  PROCEDURE ins(ir_emp IN c_emp%ROWTYPE)
  IS
    cv_proc_name CONSTANT VARCHAR2(30) := 'ins';
  BEGIN
    -- salの値チェック
    IF ir_emp.sal > 1000 THEN
      -- ユーザー定義例外の呼出
      RAISE expt_sal_over_range;
    END IF;
    INSERT INTO emp_sample
    VALUES(ir_emp.empno, ir_emp.ename, ir_emp.deptno, ir_emp.dname, ir_emp.sal, ir_emp.comm);
  EXCEPTION
    -- ユーザー定義例外での例外処理
    -- エラーコードにPRAGMAで宣言した値が設定される
--    WHEN expt_sal_over_range THEN
--      dbms_output.put_line(SQLERRM);
--      RAISE expt_sal_over_range;
    WHEN OTHERS THEN
      dbms_output.put_line(cv_proc_name || ' : ' || SQLERRM);
      RAISE;
  END ins;
--
  PROCEDURE sel(iv_dept IN VARCHAR2)
  IS
    cv_proc_name CONSTANT VARCHAR2(30) := 'sel';
    r_emp c_emp%ROWTYPE;
  BEGIN
    FOR r_emp IN c_emp(iv_dept)
    LOOP
      ins(r_emp);
    END LOOP;
  EXCEPTION
    WHEN OTHERS THEN
      dbms_output.put_line(cv_proc_name || ' : ' || SQLERRM);
      RAISE;
  END sel;
--
  PROCEDURE main(iv_dept IN VARCHAR2)
  IS
    cv_proc_name CONSTANT VARCHAR2(30) := 'main';
  BEGIN
    sel(iv_dept);
  EXCEPTION
    WHEN OTHERS THEN
      dbms_output.put_line(cv_proc_name || ' : ' || SQLERRM);
      RAISE;
  END main;
END expt_sample;
/

実行結果(※メッセージは定義していないので出力されない)

----------------------------------------
ins : ORA-06502: PL/SQL: 数値または値のエラーが発生しました
sel : ORA-06502: PL/SQL: 数値または値のエラーが発生しました
main : ORA-06502: PL/SQL: 数値または値のエラーが発生しました

----------------------------------------

冒頭の、PRAGMA EXCEPTION_INITでのユーザー定義例外の割り当てを、既存のコード-6502から、-20001に変えてみます。

--  PRAGMA EXCEPTION_INIT(expt_sal_over_range, -6502);  -- ここでユーザー定義例外に割り当て
  PRAGMA EXCEPTION_INIT(expt_sal_over_range, -20001);

すると、出力結果は下記のようになります。

----------------------------------------
ins : ORA-20001: 
sel : ORA-20001: 
main : ORA-20001: 

----------------------------------------

-20001のコードにはメッセージは割り当てられていませんので、文言は出力されません。
エラーメッセージを定義したい場合は、次回に説明するRAISE_APPLICATION_ERRORを使用することで実現可能です。

尚、エラーコードに変数や定数を使用することはできません。
下記のような記述はコンパイルエラーとなります。

----------------------------------------
  cn_err_code CONSTANT NUMBER := -20001;
  PRAGMA EXCEPTION_INIT(expt_sal_over_range, cn_err_code);
----------------------------------------
  ↓
PLS-00702: PRAGMA EXCEPTION_INITへの第2引数は数値リテラルである必要があります。

冒頭でも書いたように、PRAGMA EXCEPTION_INITを用いて多数のエラーコードを管理することは難しく、特に大規模アプリケーションの開発において、このような形でPRAGMA EXCEPTION_INITを使用することは推奨できません。

2020-01-12

Posted by tfurukaw