Reputation: 11
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 table has date and amount, and I want to amount as o/p for latest date available in ES for date given.
Eg: If date_in='20160223' and amount in ES is available for '20160220', then this amt should be returned in v_at and above while loop should exit. But it is going infinitely. Please suggest the correction in code required.
Upvotes: 0
Views: 1154
Reputation: 94914
In case there is no date1 <= date_in
you will search on forever. In order to find the last amt
for date1 <= date_in
you should use Oracle SQL's 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;
As you see, the PL/SQL function is only needed now to react on invalid date strings. Otherwise pure SQL would suffice. You may want to consider validating the date string somewhere else in PL/SQL (and give a proper error message in case it is invalid) and then use the mere SQL query with the date got instead of a PL/SQL function. (See also Mottor's comment on WHEN OTHERS
and my answer to that.)
Upvotes: 0
Reputation: 1948
change:
EXCEPTION when NO_DATA_FOUND
then
v_at:=0;
with:
EXCEPTION when NO_DATA_FOUND
then
exit;
Infinite loop happens, because at some i there is always no_data_found exception and v_at is always 0. You can write it
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;
Try not to use EXCEPTION WHEN OTHERS. When OTHERS happens, you want to see it. And if a function is often called, try to avoid exceptions. They have to be actually exception. They are expensive. When you expect NO_DATA_FOUND, instead of select into open cursor and use %NOTFOUND or use for loop.
Upvotes: 1
Reputation: 1943
What happens if there is no prior value?
Wouldn't it be simpler, faster (and safer) to do:
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)
There is no loop, only two index seeks (provided there is an index on date1). Also you don't mention if date1 is unique (but your code would also fail if not).
Upvotes: 2