2015年11月15日日曜日

PLSQL 例外処理

PLSQLの例外はEXCEPTION部で処理します。
DECLARE
    宣言部
    
BEGIN
    実行部

EXCEPTION
    例外処理部
    
END;

実行部で例外が発生すると、例外が発生した以降の処理は実行されず、例外処理部に処理が移ります。
例外処理部では、例外を補足する例外ハンドラを定義し、例外に応じた処理を行います。

例外ハンドラは以下のような構文で定義します。
WHEN 例外名 THEN
複数の例外に対して同じ処理を行いたい場合は、例外名称をORで繋ぎます。
WHEN 例外名1 OR 例外名2 THEN

まとめると以下のようになります。
DECLARE

BEGIN
    
    例外発生!EXCEPTION部へ処理が遷移

    例外発生以降の処理は行われない

EXCEPTION
    
    WHEN 例外名1 THEN
        例外1が発生時の処理
        
    WHEN 例外名2 OR 例外名3 THEN
        例外2、例外3が発生した時の処理
END;

例外の発生を確認してみます。
以前の記事「PLSQL SQL Developer からテーブル作成 」で作成したテーブルからMemberId=99のMemberNameを取得します。
しかしMemberId=99のデータがないので例外が発生します。
DECLARE
    name Member.MemberName%Type;
    
BEGIN
    
    SELECT MemberName INTO name 
        FROM Member WHERE MemberId = 99;
        
    SYS.DBMS_OUTPUT.PUT_LINE('MemberId=99のMemberName=' || name);

END;
6行目で「ORA-01403 no data found データが見つかりません。」とエラーが発生します。

例外処理を加えてみます。
DECLARE
    name Member.MemberName%Type;
    
BEGIN
    
    SELECT MemberName INTO name 
        FROM Member WHERE MemberId = 99;
        
    SYS.DBMS_OUTPUT.PUT_LINE('MemberId=99のMemberName=' || name);

EXCEPTION
    WHEN no_data_found THEN
      SYS.DBMS_OUTPUT.PUT_LINE('例外が発生しました。SQLCODE=' || SQLCODE || '、エラーメッセージ=' || SQLERRM);  
END;
今度はエラーが発生せず「例外が発生しました。SQLCODE=100、エラーメッセージ=ORA-01403: データが見つかりません」と表示されます。


EXCEPTION部で使用されている
SQLCODEはOracleのエラー番号を返す関数です。
SQLERRMはOracleのエラーメッセージを返す関数です。

WHEN no_data_found THEN はSELECT INTO文でデータが取得できなかった時の例外をキャッチします。


no_data_found以外にも、PLSQLでは最低限必要な例外が事前定義されています。
事前定義されている例外は以下のようなものがあります。
例外OracleエラーSQLCODE値発生原因
ACCESS_INTO_NULL06530-6530プログラムが未初期化オブジェクトの属性に値を代入しようとしたとき。
CASE_NOT_FOUND06592-6592CASE文のWHEN句で何も選択されておらず、ELSE句もない場合。
COLLECTION_IS_NULL06531-6531プログラムがEXISTS以外のコレクション・メソッドを未初期化のネストした表またはVARRAYに適用しようとしたか、または未初期化のネストした表またはVARRAYの要素に値を代入しようとしたとき。
CURSOR_ALREADY_OPEN06511-6511すでにオープンされているカーソルをオープンしようとしたとき。
カーソルをオープンするには、一度クローズする必要があります。
カーソルFORループは、参照するカーソルを自動的にオープンします。
このため、ループの内側ではカーソルをオープンできません。
DUP_VAL_ON_INDEX00001-1UNIQUE索引によって制約されている列に、重複した値を格納しようとしたとき。
INVALID_CURSOR01001-1001オープンされていないカーソルをクローズするなど、不正なカーソル操作を実行しようとしたとき。
INVALID_NUMBER01722-1722SQL文の中で、文字列が正しい数値を表していなかったために、文字列から数値への変換が失敗したとき。
(プロシージャ文では、VALUE_ERRORが呼び出されます。)
この例外は、バルクFETCH文のLIMIT句の式が正数に評価されない場合にも呼び出されます。
LOGIN_DENIED01017-1017不正なユーザー名またはパスワードでデータベースにログオンしようとした場合。
NO_DATA_FOUND01403+100SELECT INTO文が行を戻さなかったとき、ネストした表で削除された要素を参照したとき、または索引付き表で未初期化の要素を参照したとき。
この例外は、いくつかのSQLファンクションで終了したことを通知するために内部的に使用されているため、問合せの一部として起動されるファンクション内部で呼び出された場合は、この例外が伝播されても信頼しないでください。
NOT_LOGGED_ON01012-1012データベースに接続していないプログラムが、データベース・コールを発行した場合。
PROGRAM_ERROR06501-6501PL/SQLに内部的な問題が発生した場合。
ROWTYPE_MISMATCH06504-65041つの代入の中に含まれるホスト・カーソル変数とPL/SQLカーソル変数の戻り型に互換性がない場合。
オープン・ホスト・カーソル変数をストアド・サブプログラムに渡すとき、実パラメータの戻り型と仮パラメータの戻り型には互換性が必要です。
SELF_IS_NULL30625-30625プログラムがMEMBERメソッドの起動を試行したが、オブジェクト型のインスタンスが初期化されなかった場合。
つまり、組込みパラメータSELFがオブジェクトを指している場合です。
このパラメータは、常にMEMBERメソッドに最初に渡されるパラメータです。
STORAGE_ERROR06500-6500PL/SQLのメモリーが足りなくなった場合、またはメモリーが破損された場合。
SUBSCRIPT_BEYOND_COUNT06533-6533コレクション中の要素数より大きい索引番号を使用してネストした表またはVARRAYの要素を参照した場合。
SUBSCRIPT_OUTSIDE_LIMIT06532-6532有効範囲外(たとえば-1)の索引番号を使用してネストした表またはVARRAYの要素を参照した場合。
SYS_INVALID_ROWID01410-1410文字列が正しいROWIDを表していなかったために、文字列からユニバーサルROWIDへの変換が失敗した場合。
TIMEOUT_ON_RESOURCE00051-51データベースがリソースを求めて待機しているときにタイムアウトが発生した場合。
TOO_MANY_ROWS01422-1422SELECT INTO文が複数の行を戻した場合。
VALUE_ERROR06502-6502算術エラー、変換エラー、切捨てエラー、またはサイズ制約エラーが発生した場合。
たとえば、列値を選択し文字変数に代入するときに、その値が変数の宣言された長さよりも長い場合、PL/SQLはその代入を停止してVALUE_ERRORを呼び出します。
プロシージャ文では、文字列から数値への変換が失敗した場合にVALUE_ERRORが呼び出されます。 (SQL文では、INVALID_NUMBERが呼び出されます。)
ZERO_DIVIDE01476-1476数値を0(ゼロ)で割ろうとしたとき。

事前に定義されていない例外を補足するには othersハンドラ を使用します。
othersハンドラはすべての例外を補足します。
他の例外をハンドリングしている場合は、一番最後にothersハンドラを書きます。
othersハンドラを他の例外ハンドリングより先に書くとエラーになります。

先ほどのコードを少し修正して、SELECTの結果が複数行返るようにします。
例外はothersハンドラでキャッチされ、エラーメッセージが表示されます。
DECLARE
    name Member.MemberName%Type;
 
BEGIN
    
    SELECT MemberName INTO name 
        FROM Member;
        
    SYS.DBMS_OUTPUT.PUT_LINE('MemberId=1のMemberName=' || name);
    
    
EXCEPTION
    WHEN no_data_found THEN
        SYS.DBMS_OUTPUT.PUT_LINE('例外が発生しました。SQLCODE=' || SQLCODE || '、エラーメッセージ=' || SQLERRM);  
    
    WHEN others THEN
        SYS.DBMS_OUTPUT.PUT_LINE('その他の例外が発生しました。SQLCODE=' || SQLCODE || '、エラーメッセージ=' || SQLERRM);  
  
END;

0 件のコメント: