I have created a simple deterministic function , and I am calling it using a select query in a cursor as shown below
CREATE TABLE TEMP
(dt DATE); 
INSERT INTO   TEMP
SELECT SYSDATE FROM DUAL CONNECT BY LEVEL<=3;   
INSERT INTO   TEMP
SELECT SYSDATE+1 FROM DUAL CONNECT BY LEVEL<=3;     
COMMIT;
--2 distinct values
SELECT DISTINCT dt from TEMP;
PACKAGE FUNCTION
CREATE OR REPLACE PACKAGE dummy_fun
AUTHID CURRENT_USER
IS
    FUNCTION get_data(
                  p_date  IN DATE)
    RETURN DATE
    DETERMINISTIC;
END dummy_fun;
/
CREATE OR REPLACE PACKAGE BODY dummy_fun
IS
     FUNCTION get_data(
                  p_date IN DATE)
    RETURN DATE
    DETERMINISTIC
    IS
        
    BEGIN
         DBMS_OUTPUT.PUT_LINE('get_data with input (p_date=>'||p_date||' called)');
        
        RETURN p_date+1;
    END get_data;  
  
END dummy_fun;
/
FUNCTION CALL - Expectation that get_data is been called only twice for 2 distinct dates , whereas , if I call this SQL only , it run only only two times
DECLARE
CURSOR get_date
IS 
SELECT dummy_fun.get_data (
               dt)  from 
TEMP;
rec get_date%ROWTYPE;
v_date date;
BEGIN
OPEN get_date;
LOOP
  FETCH get_date INTO rec;
  EXIT WHEN get_date%NOTFOUND;
  NULL;
END LOOP;
CLOSE get_date;
END;
/
OUTPUT
get_data with input (p_date=>14-APR-21 called)
get_data with input (p_date=>14-APR-21 called)
get_data with input (p_date=>14-APR-21 called)
get_data with input (p_date=>14-APR-21 called)
get_data with input (p_date=>24-APR-21 called)
get_data with input (p_date=>24-APR-21 called)
get_data with input (p_date=>24-APR-21 called)
get_data with input (p_date=>24-APR-21 called)
**WITH BELOW CHANGES IT IS WORKING IN CURSOR **
CHANGE 1 - IF THE FUNCTION IS CALLED IN THE WHERE CLAUSE 
CURSOR get_date
IS 
SELECT 1  from 
TEMP
WHERE trunc(sysdate+1)= trunc(ae9_common_code.dummy_fun.get_data (
               dt))
CHANGE 2 - Kind of Scalar subquery 
CURSOR get_date
IS 
SELECT * FROM (
SELECT ae9_common_code.dummy_fun.get_data (
               dt) from 
TEMP
WHERE 1=1)
CHANGE 3 - BULK COLLECT 
SELECT ae9_common_code.dummy_fun.get_data (
               dt) BULK COLLECT INTO v_dates from 
TEMP
WHERE 1=1;
##OUTPUT FOR ALL THE ABOVE CHANGES ARE##
get_data with input (p_date=>14-APR-21 called)
get_data with input (p_date=>24-APR-21 called)
                In short, Deterministic functions caching depends on fetch size(arraysize) – results cached only within one fetch call, ssc(scalar subquery caching) has no this limitation.
Please read my series of articles about deterministic functions:
When you have:
open cur;
loop
  fetch cur into ...
end loop;
The database is fetching exactly one row at a time. As @SayanMalakshinov has noted, the database does not cache deterministic results across fetches.
What might help make this clearer is using bulk collect with a limit to get 1, 2 or more rows at a time:
create or replace procedure fetch_rows ( num_rows int ) as
  cursor get_date is 
    select dummy_fun.get_data ( dt )  
    from   temp;
  
  type rec_tab is table of get_date%rowtype
    index by pls_integer;
  rws rec_tab;
begin
  open get_date;
  loop
    fetch get_date 
    bulk collect into rws
    limit num_rows;
    exit when get_date%notfound;
  end loop;
  close get_date;
end;
/
exec fetch_rows ( 1 );
get_data with input (p_date=>14-APR-2021 10:32:36 called)
get_data with input (p_date=>14-APR-2021 10:32:36 called)
get_data with input (p_date=>14-APR-2021 10:32:36 called)
get_data with input (p_date=>15-APR-2021 10:32:36 called)
get_data with input (p_date=>15-APR-2021 10:32:36 called)
get_data with input (p_date=>15-APR-2021 10:32:36 called)
exec fetch_rows ( 2 );
get_data with input (p_date=>14-APR-2021 10:32:36 called)
get_data with input (p_date=>14-APR-2021 10:32:36 called)
get_data with input (p_date=>15-APR-2021 10:32:36 called)
get_data with input (p_date=>15-APR-2021 10:32:36 called)
exec fetch_rows ( 3 );
get_data with input (p_date=>14-APR-2021 10:32:36 called)
get_data with input (p_date=>15-APR-2021 10:32:36 called)
With a limit of 1, every row is a new fetch, so there's no caching. Set it to 2 and (potentially) every other row is cached. Up it to three and there's up to 2 rows cached per fetch, etc.
Single row fetches are slow for a whole bunch of other reasons too, so really you should be looking to use bulk collect with a limit of at least 100 anyway.
Note that the PL/SQL engine optimizes cursor-for loops to fetch 100 rows at a time, so you could also get the caching effect by writing the loop like this:
begin
  for rws in ( 
    select dummy_fun.get_data ( dt )  
    from   temp
  ) 
  loop
    null;
  end loop;
end;
/
get_data with input (p_date=>14-APR-2021 10:32:36 called)
get_data with input (p_date=>15-APR-2021 10:32:36 called)
                        If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With