|
上面提到写成PL/SQL函数之后context switch造成性能下降,下面来测试一下几种减少context switch的方法。
先尝试把函数定义在WITH里面。先不执行INSERT, 用COUNT测试一下:
WITH
function f_get_type (p_id in number) return number
as
v_ret number;
begin
select case when start_id<=p_id then type else null end as type
into v_ret
from
(select /*+ index(range_b idx_range_b_1) */ type,start_id
from range_b
where end_id>=p_id
order by end_id
) where rownum=1;
return v_ret;
end f_get_type;
select count(*)
from
(
select id,f_get_type(id) as type
from range_a a
) where type is not null
/
SP2-0642: SQL*Plus internal error state 2147, context 0:0:0
Unsafe to proceed
Elapsed: 00:00:00.02
上面这个错误是SQLPLUS报的,比较奇怪。改了几次之后发现是因为函数里面的SELECT有可能引发NO_DATA_FOUND异常。平常的PL/SQL函数在SQL里面调用时,NO_DATA_FOUND会被SQL引擎忽略,当作返回NULL处理。但是写在WITH里面就会出这个奇怪的错。为了避免NO_DATA_FOUND,在外面套个MAX函数:
WITH
function f_get_type (p_id in number) return number
as
v_ret number;
begin
select max(case when start_id<=p_id then type else null end) as type
into v_ret
from
(select /*+ index(range_b idx_range_b_1) */ type,start_id
from range_b
where end_id>=p_id
order by end_id
) where rownum=1;
return v_ret;
end f_get_type;
select count(*)
from
(
select id,f_get_type(id) as type
from range_a a
) where type is not null
/
COUNT(*)
----------
111824
Elapsed: 00:00:06.05
比起原来写法少了5秒,相当可观了。下面尝试INSERT:
insert into range_result(id,type)
WITH
function f_get_type (p_id in number) return number
as
v_ret number;
begin
select max(case when start_id<=p_id then type else null end) as type
into v_ret
from
(select /*+ index(range_b idx_range_b_1) */ type,start_id
from range_b
where end_id>=p_id
order by end_id
) where rownum=1;
return v_ret;
end f_get_type;
select *
from
(
select id,f_get_type(id) as type
from range_a a
) where type is not null
/
WITH
*
ERROR at line 2:
ORA-32034: unsupported use of WITH clause
可惜INSERT...SELECT不支持WITH里面写函数。
下面再来尝试PRAGMA UDF:
create or replace function f_get_type (p_id in number) return number
as
PRAGMA UDF; ---------- 这一行是关键
v_ret number;
begin
select case when start_id<=p_id then type else null end as type
into v_ret
from
(select /*+ index(range_b idx_range_b_1) */ type,start_id
from range_b
where end_id>=p_id
order by end_id
) where rownum=1;
return v_ret;
end f_get_type;
/
insert into range_result(id,type)
select *
from
(
select id,f_get_type(id) as type
from range_a a
) where type is not null
/
111824 rows created.
Elapsed: 00:00:11.09
和不加PRAGMA并没有什么区别。 |
|