在生产环境中,随着数据量的变化、查询条件的不同,有时执行计划发生了突变而导致SQL性能急剧下降。此时如果调整代码,或者修改业务逻辑,一方面时间上可能来不及,另一方面可能成本较高,那么有没有办法,可以在不调整代码或业务逻辑的情况下,让SQL语句使用计划突变之前的执行计划呢?本篇将介绍通过SQL_PROFILE来固化执行计划的三种方法。
一 绑定已存在的执行计划
DECLARE
v_ar_profile_hints sys.sqlprof_attr;
v_clsql_text CLOB;
BEGIN
SELECT extractvalue(VALUE(d), '/hint') AS outline_hints
BULK COLLECT
INTO v_ar_profile_hints
FROM xmltable('/*/outline_data/hint' passing
(SELECT xmltype(other_xml) AS xmlval
FROM dba_hist_sql_plan
WHERE sql_id = 'fzbu6pashquvv'
AND plan_hash_value = '116091679'
AND other_xml IS NOT NULL)) d;
SELECT sql_text
INTO v_clsql_text
FROM dba_hist_sqltext
WHERE sql_id = 'fzbu6pashquvv';
dbms_output.put_line(v_clsql_text);
dbms_sqltune.import_sql_profile(sql_text => v_clsql_text,
profile => v_ar_profile_hints,
NAME => 'test_sql',
force_match => TRUE,
REPLACE => TRUE);
END;
select * FROM dba_sql_profiles;
select * FROM v$sql;
二 手动绑定Outline信息
1 下面的SQL执行全表扫描;
2 强制使用索引执行该语句,然后通过v_sqlarea找到其对应的SQL_ID;
select /*+ index(t IDX_TEST_OWNER)*/ sum(t.object_id) FROM t_test t
where t.owner='PUBLIC';
3 查看器执行计划
SELECT *
FROM TABLE(dbms_xplan.display_cursor(sql_id => '8ctw5gjz6z75g',
cursor_child_no => NULL,
format => 'outline'));
4 绑定Outline
DECLARE
v_ar_profile_hints sys.sqlprof_attr;
v_clsql_text CLOB;
BEGIN
v_ar_profile_hints := sys.sqlprof_attr('OUTLINE_LEAF(@"SEL$1")',
'INDEX_RS_ASC(@"SEL$1" "T"@"SEL$1" ("T_TEST"."OWNER"))');
SELECT t.sql_text
INTO v_clsql_text
FROM v$sqlarea t
WHERE t.sql_id = 'dtzdyatm69v0f';
dbms_output.put_line(v_clsql_text);
dbms_sqltune.import_sql_profile(sql_text => v_clsql_text,
profile => v_ar_profile_hints,
NAME => 'test_sql',
force_match => TRUE,
REPLACE => TRUE);
END;
注:需要DBA权限。
5 查看绑定后的执行计划
SELECT t.name, t.sql_text, t.status
FROM dba_sql_profiles t
WHERE t.name = 'test_sql';
SELECT t.sql_text, t.sql_id, t.sql_profile
FROM v$sql t
WHERE t.sql_id = 'dtzdyatm69v0f';
三 借助自动优化任务
1 查看优化建议
DECLARE
v_tuning_task VARCHAR2(100);
v_sql_id v$session.prev_sql_id%TYPE := 'dtzdyatm69v0f';
v_task VARCHAR2(30);
v_report CLOB;
BEGIN
v_task := dbms_sqltune.create_tuning_task(sql_id => v_sql_id);
dbms_output.put_line('Task is ' || v_task);
v_tuning_task := v_task;
dbms_sqltune.execute_tuning_task(v_task);
dbms_output.put_line('Tuning task is ' || v_tuning_task);
SELECT dbms_sqltune.report_tuning_task(task_name => v_task)
INTO v_report
FROM dual;
dbms_output.put_line('Report is ' || v_report);
END;
2 接受优化建议
BEGIN
dbms_sqltune.accept_sql_profile(task_name => v_tuning_task,
REPLACE => TRUE,
force_match => TRUE);
END;