简体   繁体   English

为什么没有触发异常NO_DATA_FOUND?

[英]Why is the exception NO_DATA_FOUND not being triggered?

So the problem i am having is that if i execute the following procedure and the cursor doesnt find the parameter being passed, it continues to execute the block (insert statement) but instead of throwing the NO_DATA_FOUND exception error it throws a parent/foreign key error. 所以我遇到的问题是,如果我执行以下过程并且游标没有找到传递的参数,它继续执行块(插入语句)但不抛出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');

While the parent foreign key error is correct, i dont want the block to execeute if the cursor doesnt return a row. 虽然父外键错误是正确的,但如果游标不返回行,我不希望该块执行。 Why is this happening? 为什么会这样?

Also, in terms of placing the COMMIT, where exactly do i tell it to COMMIT? 另外,在放置COMMIT方面,我告诉它究竟在哪里进行COMMIT? Before the exception or after? 在例外或之后?

This is for a uni assignment. 这是一个单一的任务。

When you loop over a cursor like that, if the cursor finds no matching rows, the loop simply doesn't execute at all. 循环遍历这样的游标时,如果游标找不到匹配的行,则循环根本不执行。 A NO_DATA_FOUND exception would only be raised if you had a SELECT ... INTO ... statement inside the BEGIN/END block that did not return any rows. 只有在BEGIN / END块内部没有返回任何行的SELECT ... INTO ...语句时,才会引发NO_DATA_FOUND异常。

Where you have the COMMIT placed now, it is part of the EXCEPTION block -- but your indentation implies that you want it to execute whether the exception occurred or not. 现在放置COMMIT的地方,它是EXCEPTION块的一部分 - 但是你的缩进意味着你想要它执行是否发生了异常。 In this case, I would just put the COMMIT immediately after the INSERT, since it only matters if the INSERT is successful. 在这种情况下,我会在INSERT之后立即放置COMMIT,因为只有INSERT成功才有意义。

"So is there no way to have the NODATAFOUND exception trigger when using a cursor, if the CTITLE parameter isnt found in the table" “因此,如果在表格中找不到CTITLE参数,那么在使用游标时无法获得NODATAFOUND异常触发器”

What you could do is test the value of V_TOTAL_COST. 你可以做的是测试V_TOTAL_COST的值。 If it is zero raise an exception, like this: 如果为零则引发异常,如下所示:

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;

This assumes you have a business rule that ACTUAL_COST cannot be zero. 这假设您有一个ACTUAL_COST不能为零的业务规则。

Alternatively, there is the clunkier workaround of incrementing a counter in the loop and testing whether it is zero after the loop. 或者,有一种笨拙的解决方法,即在循环中递增计数器并测试循环后它是否为零。

As for where to place the commit I would say the answer is not inside the procedure . 至于在何处放置提交,我会说答案不在程序内 The client (sqlplus in this case) should determine if the transaction will commit or rollback as the call to end the campaign may be just a part of a wider process. 客户端(在这种情况下为sqlplus)应确定事务是否提交或回滚,因为结束活动的调用可能只是更广泛流程的一部分。 Also assuming that a campaign can exist without any advertisements then I would have an explicit check that the campaign title is valid perhaps against the table of CAMPAIGN? 同时假设一个广告系列可以在没有任何广告的情况下存在,那么我会明确检查广告系列标题是否有效,或许是针对CAMPAIGN表? as suggested below: 如下所示:

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;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM