SQL: обработка исключений в функции

Сегодня столкнулся с довольно загадочной проблемой. Когда я выполнил свою функцию SQL f_interestrate() (которая должна вызвать определенное мной исключение, когда один из параметров равен 0) со следующими параметрами:

SELECT GENERAL_FUNCTIONS.F_INTERESTRATE(2500000, 0.10, 0) FROM dual;

Дал мне следующую ошибку:

ORA-06503: PL/SQL: функция возвращена без значения
ORA-06512: в "NOAHBASE.GENERAL_FUNCTIONS", строка 73
06503. 00000 - "PL/SQL: функция возвращена без значения"
*Причина : Вызов функции PL/SQL завершен, но инструкция RETURN не выполнена.

*Действие: Перепишите функцию PL/SQL, убедившись, что она всегда возвращает значение надлежащего типа.

Но, как вы можете видеть в следующем примере кода, функция должна вместо этого вызывать форму, определенную мной как исключение ex_invalid_devisor. Не забудьте упомянуть, что эта функция вложена в пакет.

 FUNCTION f_interestrate(pn_principal NUMBER, pn_interest NUMBER, pn_years NUMBER) RETURN NUMBER IS 
      vn_interestrate NUMBER;
      ex_invalid_devisor EXCEPTION;
    BEGIN
      IF pn_principal = 0 OR 
         pn_interest = 0 OR 
         pn_years = 0 THEN 
          RAISE ex_invalid_devisor;
      ELSE
        vn_interestrate := ((pn_interest/pn_principal)-1)/pn_years;
        RETURN vn_interestrate;
      END IF;
    EXCEPTION
      WHEN ex_invalid_devisor THEN
        DBMS_OUTPUT.PUT_LINE('Devisor must be bigger then 0');
    END;

Я делаю что-то не так?


person Community    schedule 18.10.2016    source источник
comment
Предложения return на случай возникновения исключения нет. Вам нужно поставить, например. a return null в блок catch или повторно вызвать исключение. Ваш текущий блок молча проглатывает его   -  person a_horse_with_no_name    schedule 18.10.2016
comment
Ваша процедура вызывает исключение, затем обрабатывает ошибку для вывода сообщения, но в этом случае у вас нет возвращаемого значения.   -  person Aleksej    schedule 18.10.2016
comment
Да, вы делаете что-то не так: ваш блок исключений ничего не делает. В функции необходимо либо вызвать ошибку (и для ошибки, определяемой пользователем, вы можете использовать RAISE_APPLICATION_ERROR для вывода определенного сообщения об ошибке), либо вернуть значение. Кроме того, если вы должны использовать DBMS_OUTPUT, это должно быть только для целей отладки, IMHO; ему нет места в производственном коде. Лично я бы выбрал какой-то другой метод отладки - если вам нужно регистрировать, произошло ли действие или нет, почему бы вместо этого не записать его в таблицу?   -  person Boneist    schedule 18.10.2016
comment
к вашему сведению, это делитель и больше, чем;)   -  person William Robertson    schedule 18.10.2016


Ответы (2)


Вот как вы должны обращаться ... Я добавил в ваш код «return -1». ХТН.

create or replace FUNCTION f_interestrate(pn_principal NUMBER, pn_interest NUMBER, pn_years NUMBER) RETURN NUMBER IS 
      vn_interestrate NUMBER;
      ex_invalid_devisor EXCEPTION;
    BEGIN
      IF pn_principal = 0 OR 
         pn_interest = 0 OR 
         pn_years = 0 THEN 
          RAISE ex_invalid_devisor;
      ELSE
        vn_interestrate := ((pn_interest/pn_principal)-1)/pn_years;
        RETURN vn_interestrate;
      END IF;
    EXCEPTION
      WHEN ex_invalid_devisor THEN
        DBMS_OUTPUT.PUT_LINE('Devisor must be bigger then 0');
        return -1;
    END;

SQL> select F_INTERESTRATE(2500000, 0.10, 0) FROM dual;

F_INTERESTRATE(2500000,0.10,0)
------------------------------
                            -1
person pahariayogi    schedule 18.10.2016
comment
Это имеет смысл только в том случае, если вызывающая сторона готова обработать -1 как ошибку; кроме того, вы должны быть уверены, что -1 не может быть допустимым возвращаемым значением - person Aleksej; 18.10.2016
comment
Это просто указывает на проблему и доказывает, что она устраняет проблему. Всегда можно изменить возвращаемое значение с «-1» на что угодно. Дело в том, что у вас не может быть функции без возврата какого-либо значения. Лучше НЕ обрабатывать исключение нулевого деления, если значение -1 или -x неприемлемо. - person pahariayogi; 18.10.2016

Поскольку в PL/SQL уже есть прекрасное исключение zero_divide, у меня возникнет соблазн просто написать функцию так:

create or replace function f_interestrate
    ( pn_principal number
    , pn_interest  number
    , pn_years     number )
    return number
as
begin
    return ((pn_interest / pn_principal) - 1) / pn_years;
end;

то вы получите сообщение об ошибке по умолчанию:

SQL> select f_interestrate(2500000, 0.10, 0) from dual;
select f_interestrate(2500000, 0.10, 0) from dual
       *
ERROR at line 1:
ORA-01476: divisor is equal to zero
ORA-06512: at "XXX.F_INTERESTRATE", line 8

Или, если вам действительно нужна индивидуальная обработка,

create or replace function f_interestrate
    ( pn_principal number
    , pn_interest  number
    , pn_years     number )
    return number
as
begin
    return ((pn_interest / pn_principal) - 1) / pn_years;
exception
    when zero_divide then
        [[[ do something here ]]]
end;

Я заметил, что вы также вызываете свое пользовательское исключение ex_invalid_devisor, когда pn_interest равно нулю, хотя это не используется в качестве делителя, поэтому, возможно, здесь есть какая-то тонкая логика, которую я упускаю.

(Редактировать: если подумать, если pn_interest равно нулю, то, возможно, вам просто нужно вернуть pn_principal независимо.)

person William Robertson    schedule 18.10.2016