问题概述
SQL中自定义函数被调用次数过多导致性能差
解决方案
利用result_cache减少函数被调用次数
(注意:使用result_cache要格外小心,有其他解决方案的时候尽量用其他解决方案)
优化案例
下面SQL要2个多小时才能跑完
INSERT INTO a
(c65)
(SELECT test_get_out_contract_number(t.plan_po_number,
t.project_number,
t.org_id)
FROM x t);
x表一共有4768314行数据
SQL> select count(*) from x;
COUNT(*)
----------
4768314
函数的传参列组合基数为2485行
SQL> select count(*)
2 from (
3 select t.plan_po_number, t.project_number, t.org_id
4 from x t
5 group by t.plan_po_number, t.project_number, t.org_id);
COUNT(*)
----------
2485
SELECT后面有自定义函数的优化方法 分享过改写SQL加速SQL查询性能
SQL> INSERT INTO a
2 (c65)
3 (SELECT (select test_get_out_contract_number(t.plan_po_number,
4 t.project_number,
5 t.org_id)
6 from dual)
7 FROM x t);
4768314 rows inserted
Executed in 25.72 seconds
除了改写SQL之外,还可以对函数启用result_cache特性来加速SQL性能
查看result_cache相关参数
SQL> select name,display_value from v$parameter where name like '%result_cache%';
NAME DISPLAY_VALUE
------------------------------ --------------------------------------------------------------------------------
result_cache_mode MANUAL
result_cache_max_size 2G
result_cache_max_result 5
result_cache_remote_expiration 0
client_result_cache_size 0
client_result_cache_lag 3000
6 rows selected
修改函数定义,对函数启用RESULT_CACHE特性RETURN VARCHAR2 RESULT_CACHE IS
CREATE OR REPLACE FUNCTION test_get_out_contract_number(p_plan_po_number IN VARCHAR2
,p_project_number IN VARCHAR2
,p_org_id IN NUMBER) RETURN VARCHAR2 RESULT_CACHE IS
l_return VARCHAR2(240);
BEGIN
IF p_plan_po_number IS NOT NULL THEN
SELECT pha.attribute1
INTO l_return
FROM po_headers_all pha
WHERE pha.type_lookup_code = 'PLANNED'
AND pha.attribute_category = 'CONTRACT'
AND pha.segment1 = p_plan_po_number
AND pha.org_id = p_org_id
AND pha.attribute1 IS NOT NULL
AND ROWNUM = 1;
ELSIF p_project_number IS NOT NULL THEN
SELECT pha.attribute1
INTO l_return
FROM po_distributions_all pda
,po_headers_all pha
,pa_projects_all ppa
WHERE pha.po_header_id = pda.po_header_id
AND pda.project_id = ppa.project_id
AND pha.type_lookup_code = 'PLANNED'
AND pha.attribute_category = 'CONTRACT'
AND ppa.segment1 = p_project_number
AND rownum = 1;
ELSE
l_return := NULL;
END IF;
RETURN l_return;
EXCEPTION
WHEN OTHERS THEN
RETURN NULL;
END test_get_out_contract_number;
原始SQL在不改写的情况下也可以26秒跑完
SQL> INSERT INTO a
2 (c65)
3 (SELECT test_get_out_contract_number(t.plan_po_number,
4 t.project_number,
5 t.org_id)
6 FROM x t);
4768314 rows inserted
Executed in 26.404 seconds
查看V$RESULT_CACHE_OBJECTS视图信息
SQL> select id,
2 type,
3 status,
4 name,
5 namespace,
6 scan_count,
7 row_count,
8 row_size_max
9 from V$RESULT_CACHE_OBJECTS
10 where name like upper('%test_get_out_contract_number%')
11 and rownum <= 10;
ID TYPE STATUS NAME NAMESPACE SCAN_COUNT ROW_COUNT ROW_SIZE_MAX
---------- ---------- --------- -------------------------------------------------------------------------------- ---------- ---------- ---------- ------------
34502 Dependency Published APPS.TEST_GET_OUT_CONTRACT_NUMBER 0 0 0
1214 Result Published "APPS"."TEST_GET_OUT_CONTRACT_NUMBER"::8."TEST_GET_OUT_CONTRACT_NUMBER"#8b39c259 PLSQL 9 1 6
382 Result Published "APPS"."TEST_GET_OUT_CONTRACT_NUMBER"::8."TEST_GET_OUT_CONTRACT_NUMBER"#8b39c259 PLSQL 2274 1 14
2415 Result Published "APPS"."TEST_GET_OUT_CONTRACT_NUMBER"::8."TEST_GET_OUT_CONTRACT_NUMBER"#8b39c259 PLSQL 0 1 21
1561 Result Published "APPS"."TEST_GET_OUT_CONTRACT_NUMBER"::8."TEST_GET_OUT_CONTRACT_NUMBER"#8b39c259 PLSQL 6224 1 2
1591 Result Published "APPS"."TEST_GET_OUT_CONTRACT_NUMBER"::8."TEST_GET_OUT_CONTRACT_NUMBER"#8b39c259 PLSQL 2 1 14
1367 Result Published "APPS"."TEST_GET_OUT_CONTRACT_NUMBER"::8."TEST_GET_OUT_CONTRACT_NUMBER"#8b39c259 PLSQL 158 1 16
274 Result Published "APPS"."TEST_GET_OUT_CONTRACT_NUMBER"::8."TEST_GET_OUT_CONTRACT_NUMBER"#8b39c259 PLSQL 8387 1 2
77654 Result Published "APPS"."TEST_GET_OUT_CONTRACT_NUMBER"::8."TEST_GET_OUT_CONTRACT_NUMBER"#8b39c259 PLSQL 5 1 2
469 Result Published "APPS"."TEST_GET_OUT_CONTRACT_NUMBER"::8."TEST_GET_OUT_CONTRACT_NUMBER"#8b39c259 PLSQL 80 1 15
10 rows selected