pl / sql块进入无限循环

时间:2016-08-08 07:09:02

标签: plsql

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表有日期和金额,我希望在ES中提供最新日期的o / p金额。

例如: 如果date_in =' 20160223' ES中的金额可用于' 20160220',然后这个amt应该在v_at及以上返回,而循环应该退出。但它是无限的。请在需要的代码中建议更正。

3 个答案:

答案 0 :(得分: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 :(得分: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;

尽量不要使用EXCEPTION。当其他人发生时,你想要看到它。如果经常调用函数,请尽量避免异常。它们实际上必须是例外。它们是昂贵的。当您期望NO_DATA_FOUND时,而不是选择打开游标并使用%NOTFOUND或用于循环。

答案 2 :(得分: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中的其他位置验证日期字符串(并在其无效时给出正确的错误消息),然后使用带有日期而不是PL / SQL函数的纯SQL查询。 (另见Mottor对WHEN OTHERS的评论以及我对此的回答。)