为什么没有触发异常NO_DATA_FOUND?

时间:2011-10-29 01:39:36

标签: sql oracle plsql

所以我遇到的问题是,如果我执行以下过程并且游标没有找到传递的参数,它继续执行块(插入语句)但不抛出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');

虽然父外键错误是正确的,但如果光标没有返回行,我不希望该块执行。为什么会这样?

另外,在放置COMMIT方面,我告诉它究竟在哪里进行COMMIT?在例外或之后?

这是一项单一的任务。

3 个答案:

答案 0 :(得分:5)

当你循环遍历这样的游标时,如果游标找不到匹配的行,那么循环根本就不会执行。只有在BEGIN / END块中没有返回任何行的SELECT ... INTO ...语句时,才会引发NO_DATA_FOUND异常。

现在放置了COMMIT,它是EXCEPTION块的一部分 - 但是你的缩进意味着你希望它执行是否发生了异常。在这种情况下,我只是在INSERT之后立即放置COMMIT,因为只有INSERT成功才有意义。

答案 1 :(得分:3)

  

“所以当没有办法让NODATAFOUND异常触发时   如果在表“

中找不到CTITLE参数,则使用游标

您可以做的是测试V_TOTAL_COST的值。如果为零则引发异常,如下所示:

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不能为零的业务规则。

或者,有一种笨拙的解决方法,即在循环中递增计数器并测试循环后它是否为零。

答案 2 :(得分: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;