2016-08-08 2 views
0
create or replace 
function f_amt(date_in in varchar2) return number 
as 
BEGIN 
DECLARE 
    v_at   ES.AMT%TYPE; 
    i    number := 0; 
    BEGIN 

     v_at := 0; 
     WHILE v_at = 0 
     LOOP 
     BEGIN 
     select nvl(AMT,0) 
     into v_at 
     from es 
     where date1 = to_date(date_in,'MM/DD/YYYY') - i; 
     i := i + 1;  
     EXCEPTION when NO_DATA_FOUND 
     then 
     v_at:=0; 
     END; 
     END LOOP; 
     RETURN v_at; 
    END; 
EXCEPTION 
    WHEN OTHERS THEN 
    RETURN 0; 
END; 

Таблица ES имеет дату и сумму, и я хочу указать как o/p для последней даты, доступной в ES для указанной даты.pl/sql блок, идущий в бесконечной петле

Например: Если дата '20160223' и сумма в ES доступны для '20160220', тогда этот символ должен быть возвращен в v_at и выше, а цикл должен выйти. Но это происходит бесконечно. Пожалуйста, предложите исправление в коде.

ответ

2

Что произойдет, если нет прежнего значения?

Не было бы проще, быстрее (и безопаснее), чтобы сделать:

select AMT 
into v_at 
from es 
where date1 = (
    select max(date1) 
    from es 
    where date1 <= to_date(date_in,'MM/DD/YYYY') 
     and AMT is not NULL 
     and AMT <> 0) 

Там нет петли, только два индекса стремится (при условии, есть индекс на date1). Также вы не указываете, уникален ли date1 (но ваш код также потерпит неудачу, если нет).

1

изменение:

EXCEPTION when NO_DATA_FOUND 
     then 
     v_at:=0; 

с:

EXCEPTION when NO_DATA_FOUND 
     then 
     exit; 

Бесконечный цикл происходит потому, что в какой-то я всегда NO_DATA_FOUND исключение и v_at всегда 0. Вы можете написать его

CREATE OR REPLACE FUNCTION f_amt (date_in IN VARCHAR2) 
    RETURN NUMBER 
AS 
BEGIN 
    FOR c1 IN ( SELECT amt 
        FROM es 
       WHERE date1 <= TO_DATE (date_in, 'MM/DD/YYYY') 
        AND nvl(amt,0) <> 0 
       ORDER BY date1 DESC) 
    LOOP 
     RETURN c1.amt; 
    END LOOP; 

    RETURN 0; 
END; 

Старайтесь не использовать ИСКЛЮЧЕНИЕ КОГДА ДРУГИЕ. Когда ДРУГИЕ случаются, вы хотите это увидеть. И если функция часто вызывается, старайтесь избегать исключений. Они должны быть фактически исключением. Они дорогие. Когда вы ожидаете NO_DATA_FOUND, вместо выбора в курсор open и используйте% NOTFOUND или используйте для цикла.

+1

Хороший совет не использовать 'WHEN OTHERS', потому что мы также будем проглатывать исключения, когда' date1' переименовывается в 'date2' например и, следовательно, никогда не знает почему функция всегда возвращает 0 внезапно. Однако «КОГДА ДРУГИЕ», очевидно, используются здесь, чтобы поймать все возможные исключения TO_DATE. До тех пор, пока задача функции должна возвращать 0 в недопустимых строках даты, требуется что-то вроде общей обработки исключений. Вот этот вопрос, посвященный именно этому: http://stackoverflow.com/questions/9836508/converting-a-string-to-date-and-raising-an-exception-when-given-string-is-invali –

+0

@ ThorstenKettner Я вижу, но я не думаю, что если вы передадите эту функцию, дату «20160223» и вы ожидаете формат «MM/DD/YYYY», будет хорошей идеей проглотить это исключение. – Mottor

+0

Вы правы; вызывающему абоненту следует сообщить, что указанная строка даты считается недействительной. Это было бы намного лучше. Это сводится к: функция проверяет строку даты и получает AMT в случае, если она действительна. Вместо этого это должны быть две отдельные задачи. –

0

В случае отсутствия date1 <= date_in вы будете искать навсегда. Чтобы найти последние amt для date1 <= date_in, вы должны использовать Oracle SQL keep dense_rank last.

create or replace function f_amt(date_in in varchar2) return number as 
    v_amt es.amt%type; 
begin 
    select max(amt) keep (dense_rank last order by date1) 
    into v_amt 
    from es 
    where date1 <= to_date(date_in,'mm/dd/yyyy') 
    and amt <> 0; 

    return v_amt; 
exception when others then 
    return 0; 
end; 

Как вы видите, функция PL/SQL нужна только сейчас, чтобы реагировать на недействительные строки даты. В противном случае было бы достаточно чистого SQL. Вы можете рассмотреть возможность проверки строки даты где-нибудь еще в PL/SQL (и дать правильное сообщение об ошибке в случае ее недействительности), а затем использовать простой SQL-запрос с полученной датой вместо функции PL/SQL. (См. Также комментарий Моттора на WHEN OTHERS и мой ответ на этот вопрос.)

+0

Наверху добавляется «begin declare» (несомненно, скопированный с OP). –

+0

Да, спасибо Уильяму, я не заметил этого при копировании кода. Я исправил это сейчас. –

Смежные вопросы