2011-10-29 55 views
2

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

而父外键的错误是正确的,我不希望块execeute如果光标犯规返回一行。这是为什么发生?

另外,就放置COMMIT而言,我到底在哪里告诉COMMIT?在例外之前或之后?

这是一个uni任务。

回答

5

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

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

+0

我想提交只有插入成功,因为它会影响我设置的其他触发器。 – Deep

+0

那么如果在表中找不到CTITLE参数,那么在使用游标时没有办法使用NODATAFOUND异常触发器? – Deep

+0

@Deep - 那么,在您的代码发布,提交只发生在异常处理程序,它将永远不会执行。所以你没有提交。如果您在INSERT之后立即执行提交,则只有在INSERT不引发异常时才会执行该提交。另外,如果您所引用的触发器是BEFORE/AFTER INSERT触发器,则它们将在执行INSERT语句期间调用,而不是在提交时调用。 –

3

“那么,有没有办法当 使用游标,如果CTITLE参数心不是在表中发现有NODATAFOUND异常触发”

你可以做的是测试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不能为零。

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

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; 
相关问题