Почему исключение NO_DATA_FOUND не вызывается? - PullRequest
2 голосов
/ 29 октября 2011

Итак, проблема, с которой я столкнулся, заключается в том, что если я выполняю следующую процедуру и курсор не находит передаваемый параметр, он продолжает выполнять блок (оператор вставки), но вместо того, чтобы выдать ошибку исключения 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 ]

5 голосов
/ 29 октября 2011

Когда вы зацикливаете курсор таким образом, если курсор не находит подходящих строк, цикл просто не выполняется вообще.Исключение NO_DATA_FOUND возникнет только в том случае, если у вас есть оператор SELECT ... INTO ... внутри блока BEGIN / END, который не возвращает ни одной строки.

Там, где вы сейчас поместили COMMIT, он является частью блока EXCEPTION -- но ваш отступ подразумевает, что вы хотите, чтобы он выполнялся независимо от того, произошло исключение или нет.В этом случае я бы просто поместил COMMIT сразу после INSERT, поскольку это имеет значение только в том случае, если INSERT успешен.

3 голосов
/ 29 октября 2011

"Таким образом, невозможно использовать триггер исключения 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 не может быть нулевым.

В качестве альтернативы, существует обходной обходной путь увеличения счетчика в цикле и проверки, равен ли он нулю после цикла.

0 голосов
/ 29 октября 2011

Что касается того, где разместить коммит, я бы сказал, что ответ не внутри процедуры .Клиент (в данном случае 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;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...