Efficient Function Calls From SQL
This article presents a number of solutions for making calls to PL/SQL functions from SQL more efficient.
oracle miscconfigurationintermediate
by OracleDba
14 views
This article presents a number of solutions for making calls to PL/SQL functions from SQL more efficient.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051
conn / as sysdba
grant execute on dbms_lock to test;
conn test/test
drop table func_test;
create table func_test (
id number
);
insert into func_test
select case
when level = 10 then 3
when mod(level, 2) = 0 then 2
else 1
end
from dual
connect by level <= 10;
commit;
create or replace function slow_function (p_in in number)
return number
as
begin
dbms_lock.sleep(1);
return p_in;
end;
/
set timing on
select slow_function(id)
from func_test;
SLOW_FUNCTION(ID)
-----------------
1
2
1
2
1
2
1
2
1
3
10 rows selected.
Elapsed: 00:00:10.14
SQL>12345678910111213141516171819202122
set timing on
select (select slow_function(id) from dual)
from func_test;
(SELECTSLOW_FUNCTION(ID)FROMDUAL)
---------------------------------
1
2
1
2
1
2
1
2
1
3
10 rows selected.
Elapsed: 00:00:03.03
SQL>123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657
create or replace function slow_function (p_in in number)
return number
deterministic
as
begin
dbms_lock.sleep(1);
return p_in;
end;
/
set timing on
set arraysize 15
select slow_function(id)
from func_test;
SLOW_FUNCTION(ID)
-----------------
1
2
1
2
1
2
1
2
1
3
10 rows selected.
Elapsed: 00:00:04.04
SQL>
set timing on
set arraysize 2
select slow_function(id)
from func_test;
SLOW_FUNCTION(ID)
-----------------
1
2
1
2
1
2
1
2
1
3
10 rows selected.
Elapsed: 00:00:10.01
SQL>12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455
create or replace function slow_function (p_in in number)
return number
result_cache
as
begin
dbms_lock.sleep(1);
return p_in;
end;
/
set timing on
select slow_function(id)
from func_test;
SLOW_FUNCTION(ID)
-----------------
1
2
1
2
1
2
1
2
1
3
10 rows selected.
Elapsed: 00:00:03.09
SQL>
set timing on
select slow_function(id)
from func_test;
SLOW_FUNCTION(ID)
-----------------
1
2
1
2
1
2
1
2
1
3
10 rows selected.
Elapsed: 00:00:00.02
SQL>123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
-- Recreate the original function to remove any amendments made in previous examples.
create or replace function slow_function (p_in in number)
return number
as
begin
dbms_lock.sleep(1);
return p_in;
end;
/
create or replace package cached_lookup_api as
function get_cached_value (p_id in number)
return number;
procedure clear_cache;
end cached_lookup_api;
/
create or replace package body cached_lookup_api as
type t_tab is table of number
index by binary_integer;
g_tab t_tab;
g_last_use date := sysdate;
g_max_cache_age number := 10/(24*60); -- 10 minutes
-- -----------------------------------------------------------------
function get_cached_value (p_id in number)
return number as
l_value number;
begin
if (sysdate - g_last_use) > g_max_cache_age then
-- Older than 10 minutes. Delete cache.
g_last_use := sysdate;
clear_cache;
end if;
begin
l_value := g_tab(p_id);
exception
when no_data_found then
-- call function and cache data.
l_value := slow_function(p_id);
g_tab(p_id) := l_value;
end;
return l_value;
end get_cached_value;
-- -----------------------------------------------------------------
-- -----------------------------------------------------------------
procedure clear_cache as
begin
g_tab.delete;
end;
-- -----------------------------------------------------------------
end cached_lookup_api;
/
set timing on
select cached_lookup_api.get_cached_value(id)
from func_test;
CACHED_LOOKUP_API.GET_CACHED_VALUE(ID)
--------------------------------------
1
2
1
2
1
2
1
2
1
3
10 rows selected.
Elapsed: 00:00:03.10
SQL>
set timing on
select cached_lookup_api.get_cached_value(id)
from func_test;
CACHED_LOOKUP_API.GET_CACHED_VALUE(ID)
--------------------------------------
1
2
1
2
1
2
1
2
1
3
10 rows selected.
Elapsed: 00:00:00.01
SQL>123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
-- Recreate the original function to remove any amendments made in previous examples.
create or replace function slow_function (p_in in number)
return number
as
begin
dbms_lock.sleep(1);
return p_in;
end;
/
create or replace context cache_context using cached_lookup_api accessed globally;
create or replace package cached_lookup_api as
function get_cached_value (p_id in number)
return number;
procedure clear_cache;
end cached_lookup_api;
/
create or replace package body cached_lookup_api as
g_last_use date := sysdate;
g_max_cache_age number := 10/(24*60); -- 10 minutes
g_context_name varchar2(20) := 'cache_context';
-- -----------------------------------------------------------------
function get_cached_value (p_id in number)
return number as
l_value number;
begin
if (sysdate - g_last_use) > g_max_cache_age then
-- Older than 10 minutes. Delete cache.
g_last_use := sysdate;
clear_cache;
end if;
l_value := sys_context(g_context_name, p_id);
if l_value is null then
l_value := slow_function(p_id);
dbms_session.set_context(g_context_name, p_id, l_value);
end if;
return l_value;
end get_cached_value;
-- -----------------------------------------------------------------
-- -----------------------------------------------------------------
procedure clear_cache as
begin
dbms_session.clear_all_context(g_context_name);
end;
-- -----------------------------------------------------------------
end cached_lookup_api;
/
set timing on
select cached_lookup_api.get_cached_value(id)
from func_test;
CACHED_LOOKUP_API.GET_CACHED_VALUE(ID)
--------------------------------------
1
2
1
2
1
2
1
2
1
3
10 rows selected.
Elapsed: 00:00:03.07
SQL>
set timing on
select cached_lookup_api.get_cached_value(id)
from func_test;
CACHED_LOOKUP_API.GET_CACHED_VALUE(ID)
--------------------------------------
1
2
1
2
1
2
1
2
1
3
10 rows selected.
Elapsed: 00:00:00.01
SQL>
-- Create new session.
conn test/test
set timing on
select cached_lookup_api.get_cached_value(id)
from func_test;
CACHED_LOOKUP_API.GET_CACHED_VALUE(ID)
--------------------------------------
1
2
1
2
1
2
1
2
1
3
10 rows selected.
Elapsed: 00:00:00.02
SQL>123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
drop table t2;
create table t2 (
id number
);
insert /*+ append */ into t2
select 1
from dual
connect by level <= 100000;
commit;
create or replace function slow_function (p_in in number)
return number
result_cache
as
begin
--dbms_lock.sleep(1);
return p_in;
end;
/
set serveroutput on
declare
l_start number;
begin
l_start := dbms_utility.get_cpu_time;
for cur_rec in (
select slow_function(id)
from t2
)
loop
null;
end loop;
dbms_output.put_line('Regular Query (SELECT List): ' ||
(dbms_utility.get_cpu_time - l_start) || ' hsecs CPU Time');
l_start := dbms_utility.get_cpu_time;
for cur_rec in (
select (select slow_function(id) from dual)
from t2
)
loop
null;
end loop;
dbms_output.put_line('Scalar Subquery (SELECT List): ' ||
(dbms_utility.get_cpu_time - l_start) || ' hsecs CPU Time');
END;
/
Regular Query (SELECT List): 42 hsecs CPU Time
Scalar Subquery (SELECT List): 6 hsecs CPU Time
PL/SQL procedure successfully completed.
SQL>
set serveroutput on
declare
l_start number;
begin
l_start := dbms_utility.get_cpu_time;
for cur_rec in (
select 1
from t2
where id = slow_function(id)
)
loop
null;
end loop;
dbms_output.put_line('Regular Query (WHERE): ' ||
(dbms_utility.get_cpu_time - l_start) || ' hsecs CPU Time');
l_start := dbms_utility.get_cpu_time;
for cur_rec in (
select 1
from t2
where id = (select slow_function(id) from dual)
)
loop
null;
end loop;
dbms_output.put_line('Scalar Subquery (WHERE): ' ||
(dbms_utility.get_cpu_time - l_start) || ' hsecs CPU Time');
END;
/
Regular Query (WHERE): 49 hsecs CPU Time
Scalar Subquery (WHERE): 13 hsecs CPU Time
PL/SQL procedure successfully completed.
SQL>123456
exec dbms_flashback.enable_at_time(dbms_flashback.get_system_change_number);
select slow_function(id)
from func_test;
exec dbms_flashback.disable;Please to add comments
No comments yet. Be the first to comment!