简体   繁体   中英

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.

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? 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.

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. In this case, I would just put the COMMIT immediately after the INSERT, since it only matters if the INSERT is successful.

"So is there no way to have the NODATAFOUND exception trigger when using a cursor, if the CTITLE parameter isnt found in the table"

What you could do is test the value of 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.

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. 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? 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;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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