2011-10-29 22 views
2

私は次のプロシージャを実行し、カーソルが渡されるパラメータを見つけることができない場合、ブロック(insert文)を実行し続けますが、NO_DATA_FOUND例外エラーをスローするのではなく、親/キーエラー。例外NO_DATA_FOUNDがトリガーされないのはなぜですか?

CREATE OR REPLACE PACKAGE ASSIGNMENT3 IS 

PROCEDURE END_CAMPAIGN(CTITLE IN CAMPAIGN.CAMPAIGNTITLE%TYPE); 

END ASSIGNMENT3; 
/ 

CREATE OR REPLACE PACKAGE BODY ASSIGNMENT3 AS  
    PROCEDURE END_CAMPAIGN(CTITLE IN CAMPAIGN.CAMPAIGNTITLE%TYPE) IS 
     CURSOR ADCOST_CUR IS 
     SELECT ACTUALCOST 
     FROM ADVERTISEMENT 
     WHERE ADVERTISEMENT.CAMPAIGNTITLE = CTITLE; 
     V_TOTALCOST NUMBER; 

     BEGIN 
     V_TOTALCOST := 0; 
      FOR INVOICE_REC IN ADCOST_CUR 
      LOOP 
      V_TOTALCOST := V_TOTALCOST + INVOICE_REC.ACTUALCOST; 
      END LOOP; 
      INSERT INTO INVOICE(INVOICENO, CAMPAIGNTITLE, DATEISSUED, DATEPAID, BALANCEOWING, STATUS) 
      VALUES (AUTOINCREMENTINVOICE.nextval, CTITLE, SYSDATE, NULL,V_TOTALCOST,NULL); 
      EXCEPTION WHEN NO_DATA_FOUND THEN 
       DBMS_OUTPUT.PUT_LINE('ERROR:The campaign title you entered returned no record(s), please enter a valid campaign title.'); 
     COMMIT; 
     END END_CAMPAIGN; 


     END ASSIGNMENT3; 
     /


     SET SERVEROUTPUT ON 
     EXECUTE ASSIGNMENT3.END_CAMPAIGN('Panasonic 3D TV'); 

親の外部キーエラーが正しいですが、私は、カーソルが行を返さdoesntの場合、ブロックはexeceuteしたくありません。なぜこうなった?

また、COMMITを置くという点では、正確にどこにCOMMITするのですか?例外の前か後?

これは単式割り当てのためのものです。

答えて

5

このようなカーソルをループすると、一致する行が見つからない場合、ループはまったく実行されません。 NO_DATA_FOUND例外は、行を返さなかったBEGIN/ENDブロック内にSELECT ... INTO ...ステートメントがある場合にのみ発生します。

ここにCOMMITを配置した場所はEXCEPTIONブロックの一部ですが、インデントは、例外が発生したかどうかを実行することを意味します。この場合、INSERTの直後にCOMMITを置くだけです。これは、INSERTが成功した場合にのみ重要です。

+0

私が設定した他のトリガーに影響するため、挿入が成功した場合にのみコミットします。 – Deep

+0

CTITLEパラメータがテーブルに見つからない場合、カーソルを使用しているときにNODATAFOUND例外トリガを持つ方法はありませんか? – Deep

+0

@Deep - まあ、投稿されたコードでは、コミットは例外ハンドラでのみ実行されますが、決して実行されません。あなたはコミットしていません。 INSERTの直後にコミットを置くと、INSERTが例外を生成しない場合にのみ実行されます。また、参照しているトリガーがBEFORE/AFTER INSERTトリガーの場合、INSERTステートメントの実行中に呼び出されます。コミットされていないトリガーは呼び出されません。 –

3

何を行う可能性がV_TOTAL_COSTの値をテストしている「だからCTITLEパラメータありえないが、テーブルで見つかった場合は、カーソルを使用した場合 NODATAFOUND例外トリガーを持ってする方法はありません」 。それは例外ゼロ昇給の場合は、次のように:

PROCEDURE END_CAMPAIGN(CTITLE IN CAMPAIGN.CAMPAIGNTITLE%TYPE) IS 
    CURSOR ADCOST_CUR IS 
    SELECT ACTUALCOST 
    FROM ADVERTISEMENT 
    WHERE ADVERTISEMENT.CAMPAIGNTITLE = CTITLE; 
    V_TOTALCOST NUMBER; 

    BEGIN 
     V_TOTALCOST := 0; 
     FOR INVOICE_REC IN ADCOST_CUR 
     LOOP 
     V_TOTALCOST := V_TOTALCOST + INVOICE_REC.ACTUALCOST; 
     END LOOP; 

     if v_total_cost = 0 then 
      raise no_data_found; 
     end if; 

     INSERT INTO INVOICE(INVOICENO, CAMPAIGNTITLE, DATEISSUED, DATEPAID, BALANCEOWING, STATUS) 
     VALUES (AUTOINCREMENTINVOICE.nextval, CTITLE, SYSDATE, NULL,V_TOTALCOST,NULL); 
     COMMIT; 
    EXCEPTION WHEN NO_DATA_FOUND THEN 
      DBMS_OUTPUT.PUT_LINE('ERROR:The campaign title you entered returned no record(s), please enter a valid campaign title.'); 

    END END_CAMPAIGN; 

これは、あなたがACTUAL_COSTはゼロにはできませんビジネスルールを前提としています。

また、ループ内のカウンタをインクリメントして、ループの後にゼロであるかどうかをテストする、頑丈な回避策があります。

0

コミットをどこに置くかについては、答えはで、手順はではないと言います。クライアント(この場合はsqlplus)は、キャンペーンを終了する呼び出しが広範なプロセスの一部にすぎないため、トランザクションがコミットまたはロールバックするかどうかを判断する必要があります。また、広告がなくてもキャンペーンが存在すると仮定すると、キャンペーンのタイトルがCAMPAIGNのテーブルに対して有効であることが明示的にチェックされますか?

CREATE OR REPLACE PACKAGE ASSIGNMENT3 IS 

    PROCEDURE END_CAMPAIGN(CTITLE IN CAMPAIGN.CAMPAIGNTITLE%TYPE); 

END ASSIGNMENT3; 
/ 

CREATE OR REPLACE PACKAGE BODY ASSIGNMENT3 AS  
    PROCEDURE END_CAMPAIGN(CTITLE IN CAMPAIGN.CAMPAIGNTITLE%TYPE) IS 

     V_VALID_CAMPAIGN INTEGER; 
     V_TOTALCOST NUMBER; 

    BEGIN 

     -- Check this campaign title is valid 
     /* Will get you NO_DATA_FOUND here if CTITLE is invalid so wrap in 
      another BEGIN END block to throw own custom error that the client 
      of this procedure can handle (if it wants) */ 
     BEGIN 
      SELECT 1 
      INTO V_VALID_CAMPAIGN 
      FROM CAMPAIGN 
      WHERE CAMPAIGNTITLE = CTITLE; 
     EXCEPTION 
      WHEN NO_DATA_FOUND THEN 
       RAISE_APPLICATION_ERROR(-20000,'The campaign title you entered returned no record(s), please enter a valid campaign title.'); 
     END; 

     -- Now tot up the cost of ads in this campaign and raise the invoice 
     SELECT SUM(ACTUALCOST) 
     INTO V_TOTALCOST 
     FROM ADVERTISEMENT 
     WHERE ADVERTISEMENT.CAMPAIGNTITLE = CTITLE; 

     INSERT INTO INVOICE(INVOICENO, CAMPAIGNTITLE, DATEISSUED, DATEPAID, BALANCEOWING, STATUS) 
     VALUES (AUTOINCREMENTINVOICE.nextval, CTITLE, SYSDATE, NULL,V_TOTALCOST,NULL); 

    END END_CAMPAIGN; 

END ASSIGNMENT3; 
/

EXECUTE ASSIGNMENT3.END_CAMPAIGN('Panasonic 3D TV'); 
COMMIT; 
関連する問題