2015年11月16日月曜日

PLSQL 例外処理のネスト

前回の「PLSQL 例外処理」に続き、
今回は例外処理のネストについてまとめます。


例外の発生が予想され、例外を適切に処理したあと正常系の処理を行いたいとします。
以下のコードでは6行目でSELECT文の結果が0件のためno_data_found例外が発生し、
「例外が発生しました。SQLCODE=100、エラーメッセージ=ORA-01403: データが見つかりません。」と表示されます。
「その後の処理」は出力されません。
DECLARE
    name Member.MemberName%Type;
 
BEGIN
    
    SELECT MemberName INTO name 
        FROM Member WHERE MemberId = 99;       
    SYS.DBMS_OUTPUT.PUT_LINE('MemberId=1のMemberName=' || name);
    
    
    SYS.DBMS_OUTPUT.PUT_LINE('その後の処理');
    
EXCEPTION
    WHEN others THEN
        SYS.DBMS_OUTPUT.PUT_LINE('例外が発生しました。SQLCODE=' || SQLCODE || '、エラーメッセージ=' || SQLERRM);  
END;

6行目のno_data_foundは予想された処理であり、データがなかったときは例外を握りつぶして正常系の処理に戻りたいとします。
そのような場合は6行目の処理を子ブロックにし例外を処理します。

以下のコードではno_data_found例外が発生してもnull文で例外を握りつぶしているので、「その後の処理」が出力されます。
(null文は「何もしない」ということ)
DECLARE
    name Member.MemberName%Type;
 
BEGIN
    
    BEGIN
        --結果が0件となるSELECT文
        SELECT MemberName INTO name 
            FROM Member WHERE MemberId = 99;       
        SYS.DBMS_OUTPUT.PUT_LINE('MemberId=1のMemberName=' || name);
    EXCEPTION
        --該当データがなければ例外を握りつぶす。
        WHEN no_data_found THEN
            null;
    END;
    
    SYS.DBMS_OUTPUT.PUT_LINE('その後の処理');
    
EXCEPTION
    WHEN others THEN
        SYS.DBMS_OUTPUT.PUT_LINE('例外が発生しました。SQLCODE=' || SQLCODE || '、エラーメッセージ=' || SQLERRM);  
END;
子ブロックで処理しなかった例外は親ブロックへ伝播(でんぱ)します。
先ほどの6行目SELECT文を修正して複数行のデータが返るようにします。
すると6行目ではtoo_many_rows例外が発生しますが、子ブロックのEXCEPTION部でハンドリングされていない例外なので
親ブロックのEXCEPTION部のothersハンドラで補足され、
「例外が発生しました。SQLCODE=-1422、エラーメッセージ=ORA-01422: 完全フェッチがリクエストよりも多くの行を戻しました。」と表示されます。
DECLARE
    name Member.MemberName%Type;
 
BEGIN
    
    BEGIN
        --結果が複数件となるSELECT文
        SELECT MemberName INTO name 
            FROM Member;       
        SYS.DBMS_OUTPUT.PUT_LINE('MemberId=1のMemberName=' || name);
    EXCEPTION
        --該当データがなければ例外を握りつぶす。
        WHEN no_data_found THEN
            null;
    END;
    
    SYS.DBMS_OUTPUT.PUT_LINE('その後の処理');
    
EXCEPTION
    WHEN others THEN
        SYS.DBMS_OUTPUT.PUT_LINE('例外が発生しました。SQLCODE=' || SQLCODE || '、エラーメッセージ=' || SQLERRM);  
END;

例外は補足して処理するが、正常系の処理には戻らず、親ブロックの例外に伝播してほしい場合もあります。
このような場合はRAISE文により、補足して例外を再発生させます。

以下のコードはカーソルを開いてデータを取得しています。
例外が発生してもカーソルが閉じられるるように、EXCEPTION部をネストして、カーソルを閉じています。
その後は正常系の処理には戻らず、RAISE文により例外を再発生させ、親ブロックのEXCEPTION部に伝播し、エラー内容を出力させています。
DECLARE
    --カーソル宣言
    CURSOR member_csr IS SELECT * FROM Member; 
    --カーソル変数
    member_rec member_csr%Rowtype;
    
    numValue NUMBER;
BEGIN
    
    --カーソル オープン
    OPEN member_csr;
    
    BEGIN
        LOOP
            FETCH member_csr INTO member_rec;
            EXIT WHEN member_csr%NOTFOUND;
            --例外発生
            numValue := 1/0;
        END LOOP;
        
        --正常系処理でのカーソルクローズ
        CLOSE member_csr;
        SYS.DBMS_OUTPUT.PUT_LINE('正常系処理内でカーソルを閉じました。');
        
    EXCEPTION
        WHEN others THEN
            --例外が発生してもカーソルをクローズするようにする
            CLOSE member_csr;
            SYS.DBMS_OUTPUT.PUT_LINE('異常系処理内でカーソルを閉じました。');
            --例外を再発生させる
            RAISE;
    END;
    
    SYS.DBMS_OUTPUT.PUT_LINE('その後の処理');
    
EXCEPTION
    WHEN others THEN
        SYS.DBMS_OUTPUT.PUT_LINE('例外が発生しました。SQLCODE=' || SQLCODE || '、エラーメッセージ=' || SQLERRM);  
END;

0 件のコメント: