oracle 提供了優化建議功能包DBMS_SQLTUNE,該包可以幫助我們分析SQL,並提供優化建議。
Sql_Profile是用來影響數據庫執行計劃生成的一組信息文件的集合,可以在不改變原有SQL語句的前提下,達到類似HINTS改變其執行計劃的目的。
原有執行計劃
alter session set statistics_level=all; set serveroutput off select * from test.emp where ename='SCOTT' and DEPTNO=20; SELECT * FROM table(dbms_xplan.display_cursor(NULL,NULL,'runstats_last')); PLAN_TABLE_OUTPUT ------------------------------------------------------------------------------------------------------------------------ SQL_ID 8k1gbrapm7zpd, child number 0 ------------------------------------- select * from test.emp where ename='SCOTT' and DEPTNO=20 Plan hash value: 3956160932 ------------------------------------------------------------------------------------ | Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | ------------------------------------------------------------------------------------ | 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.01 | 4 | |* 1 | TABLE ACCESS FULL| EMP | 1 | 1 | 1 |00:00:00.01 | 4 | ------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter(("ENAME"='SCOTT' AND "DEPTNO"=20))
下面就用DBMS_SQLTUNE優化該SQL
--1.賦予用戶ADVISOR權限
grant ADVISOR to test;
--2.創建sql tuning任務
conn test/test
DECLARE my_task_name VARCHAR2(30); my_sqltext CLOB; BEGIN my_sqltext := 'select * from emp where ename= :name and DEPTNO= :deptno'; my_task_name := DBMS_SQLTUNE.CREATE_TUNING_TASK( sql_text => my_sqltext, bind_list => sql_binds(anydata.convertvarchar2(10),anydata.convertnumber(2)), user_name => 'TEST', scope => 'COMPREHENSIVE', time_limit => 60, task_name => 'test_sql_tuning', description => 'Task to tune a query on emp'); END; /
注意:
記住,DBMS_SQLTUNE.CREATE_TUNING_TASK是一個函數,必須要有返回值。
上面在定義了一個TASK後,可通過DBMS_SQLTUNE.EXECUTE_TUNING_TASK來執行此調優過程。
bind_list:
多個綁定變量以','逗號分隔。參數值一定要根據綁定變量對應的列的類型書寫.
如:emp.ename類型是VARCHAR2(10),那麼就要寫成
bind_list =>sql_binds(anydata.convertvarchar2(10)),
time_limit:
執行的最長時間,默認是60。
scope:
LIMITED,用大概1秒時間去優化SQL語句,但是並不進行SQL Profiling分析。
COMPREHENSIVE,進行全面分析,包含SQL Profiling分析;比LIMITED用時更長。
**也可以用sql_id創建sql tunning任務,比用sql_text方便很多
FUNCTION CREATE_TUNING_TASK RETURNS VARCHAR2 Argument Name Type In/Out Default? ------------------------------ ----------------------- ------ -------- SQL_ID VARCHAR2 IN PLAN_HASH_VALUE NUMBER IN DEFAULT SCOPE VARCHAR2 IN DEFAULT TIME_LIMIT NUMBER IN DEFAULT TASK_NAME VARCHAR2 IN DEFAULT DESCRIPTION VARCHAR2 IN DEFAULT DECLARE my_task_name VARCHAR2(30); BEGIN my_task_name := DBMS_SQLTUNE.CREATE_TUNING_TASK( SQL_ID => 'ddw7j6yfnw0vz', scope => 'COMPREHENSIVE', ----------->任務類型,有limited和comprehensive兩種 time_limit => 60, ----------->此任務最長的執行時間 task_name => 'tunning_task_ddw7j6yfnw0vz', ----------->任務名 description => 'Task to tune a query on ddw7j6yfnw0vz' ----------->任務描述 ); END; /
--3.查看任務名 SELECT TASK_NAME
FROM DBA_ADVISOR_LOG
WHERE OWNER = 'TEST';
TASK_NAME
------------------------------
test_sql_tuning
--4.執行sql tuning任務
BEGIN DBMS_SQLTUNE.EXECUTE_TUNING_TASK( task_name => 'test_sql_tuning', ------------>任務名 execution_name => NULL, ----------->執行時的名稱,可爲空 execution_params => NULL, ------------>執行參數,默認可爲空 execution_desc => NULL ------------>執行描述 );END;/
--5.查看sql tunning任務狀態
SELECT status FROM USER_ADVISOR_TASKS WHERE task_name = 'test_sql_tuning'; STATUS ----------- COMPLETED
--6.展示sql tunning結果
SET LONG 10000 SET LONGCHUNKSIZE 1000 SET LINESIZE 100 SELECT DBMS_SQLTUNE.REPORT_TUNING_TASK('test_sql_tuning') FROM DUAL;
DBMS_SQLTUNE.REPORT_TUNING_TASK('TEST_SQL_TUNING') ---------------------------------------------------------------------------------------------------- GENERAL INFORMATION SECTION ------------------------------------------------------------------------------- Tuning Task Name : test_sql_tuning Tuning Task Owner : TEST Workload Type : Single SQL Statement Scope : COMPREHENSIVE Time Limit(seconds): 60 Completion Status : COMPLETED Started at : 04/01/2014 16:45:16 Completed at : 04/01/2014 16:45:17 DBMS_SQLTUNE.REPORT_TUNING_TASK('TEST_SQL_TUNING') ---------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------- Schema Name: TEST SQL ID : 95fv6dbj64d0f SQL Text : select * from emp where ename= :name and DEPTNO= :deptno ------------------------------------------------------------------------------- FINDINGS SECTION (2 findings) ------------------------------------------------------------------------------- 1- Statistics Finding --------------------- DBMS_SQLTUNE.REPORT_TUNING_TASK('TEST_SQL_TUNING') ---------------------------------------------------------------------------------------------------- Table "TEST"."EMP" was not analyzed. Recommendation -------------- - Consider collecting optimizer statistics for this table. execute dbms_stats.gather_table_stats(ownname => 'TEST', tabname => 'EMP', estimate_percent => DBMS_STATS.AUTO_SAMPLE_SIZE, method_opt => 'FOR ALL COLUMNS SIZE AUTO'); Rationale --------- DBMS_SQLTUNE.REPORT_TUNING_TASK('TEST_SQL_TUNING') ---------------------------------------------------------------------------------------------------- The optimizer requires up-to-date statistics for the table in order to select a good execution plan. 2- Index Finding (see explain plans section below) -------------------------------------------------- The execution plan of this statement can be improved by creating one or more indices. Recommendation (estimated benefit: 66.67%) ------------------------------------------ - Consider running the Access Advisor to improve the physical schema design DBMS_SQLTUNE.REPORT_TUNING_TASK('TEST_SQL_TUNING') ---------------------------------------------------------------------------------------------------- or creating the recommended index. create index TEST.IDX$$_00D80001 on TEST.EMP("ENAME","DEPTNO"); Rationale --------- Creating the recommended indices significantly improves the execution plan of this statement. However, it might be preferable to run "Access Advisor" using a representative SQL workload as opposed to a single statement. This will allow to get comprehensive index recommendations which takes into account index maintenance overhead and additional space consumption. DBMS_SQLTUNE.REPORT_TUNING_TASK('TEST_SQL_TUNING') ---------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------- EXPLAIN PLANS SECTION ------------------------------------------------------------------------------- 1- Original ----------- Plan hash value: 3956160932 -------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | -------------------------------------------------------------------------- DBMS_SQLTUNE.REPORT_TUNING_TASK('TEST_SQL_TUNING') ---------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 87 | 3 (0)| 00:00:01 | |* 1 | TABLE ACCESS FULL| EMP | 1 | 87 | 3 (0)| 00:00:01 | -------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter("ENAME"=:NAME AND "DEPTNO"=:DEPTNO) 2- Using New Indices -------------------- DBMS_SQLTUNE.REPORT_TUNING_TASK('TEST_SQL_TUNING') ---------------------------------------------------------------------------------------------------- Plan hash value: 2106247215 ---------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ---------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 87 | 1 (0)| 00:00:01 | | 1 | TABLE ACCESS BY INDEX ROWID| EMP | 1 | 87 | 1 (0)| 00:00:01 | |* 2 | INDEX RANGE SCAN | IDX$$_00D80001 | 1 | | 1 (0)| 00:00:01 | ---------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): DBMS_SQLTUNE.REPORT_TUNING_TASK('TEST_SQL_TUNING') ---------------------------------------------------------------------------------------------------- --------------------------------------------------- 2 - access("ENAME"=:NAME AND "DEPTNO"=:DEPTNO) ------------------------------------------------------------------------------- 建議報告總結: <1>收集EMP表的統計信息 execute dbms_stats.gather_table_stats(ownname => 'TEST', tabname =>'EMP', estimate_percent => DBMS_STATS.AUTO_SAMPLE_SIZE,method_opt => 'FOR ALL COLUMNS SIZE AUTO'); <2>創建索引 create index TEST.IDX$$_00D80001 on TEST.EMP("ENAME","DEPTNO"); 優化後執行計劃 -------------------------------------------------------------------------------------------------------- | Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | -------------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.01 | 3 | | 1 | TABLE ACCESS BY INDEX ROWID| EMP | 1 | 1 | 1 |00:00:00.01 | 3 | |* 2 | INDEX RANGE SCAN | IDX$$_00D80001 | 1 | 1 | 1 |00:00:00.01 | 2 | -------------------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("ENAME"='SCOTT' AND "DEPTNO"=20)
ACCEPT_SQL_PROFILE #接受及應用一個SQL_PROFILE執行計劃給某條SQL
DBMS_SQLTUNE.ACCEPT_SQL_PROFILE ( task_name IN VARCHAR2, -------------->執行優化的任務名 object_id IN NUMBER := NULL, -------------->對象編號,一般不填 name IN VARCHAR2 := NULL, -------------->制定的sql_profile名稱,如果不填則由系統指派 description IN VARCHAR2 := NULL, -------------->該執行計劃的描述信息 category IN VARCHAR2 := NULL); ------------->需要與該SESSION的sqltune_category參數相匹配 task_owner IN VARCHAR2 := NULL, ------------->任務的所有者 replace IN BOOLEAN := FALSE, ------------->如果此sql_profile已存在,則決定是否替換,默認值爲不替換 force_match IN BOOLEAN := FALSE, ------------->是否強制匹配此執行計劃與所有HASH值相同的SQL,類似CURSOR_SHARING參數的FORCE profile_type IN VARCHAR2 := REGULAR_PROFILE); ------------->sql_profile的類型,默認爲REGULAR_PROFILE,可修改爲PX_PROFILE,表示此執行計劃變更爲並行執行 DROP_SQL_PROFILE DBMS_SQLTUNE.DROP_SQL_PROFILE ( #刪除一個SQL_PROFILE的應用,讓系統自動選擇 name IN VARCHAR2, ignore IN BOOLEAN := FALSE);
--7.完成後刪除sql tunning任務
EXEC DBMS_SQLTUNE.DROP_TUNING_TASK('test_sql_tuning');
--8.其他
--sql tunning任務創建後,也可以修改參數 BEGIN DBMS_SQLTUNE.SET_TUNING_TASK_PARAMETER( task_name => 'test_sql_tuning', parameter => 'TIME_LIMIT', value => 300); END; / --查看SQL Tuning Advisor的進展(task執行很久) col opname for a20 col ADVISOR_NAME for a20 SELECT SID,SERIAL#,USERNAME,OPNAME,ADVISOR_NAME,TARGET_DESC,START_TIME SOFAR, TOTALWORK FROM V$ADVISOR_PROGRESS WHERE USERNAME = 'TEST';