概述:有多種方法可用來找出哪些sql語句需要優化,最簡單的方法都是分析保存在v$sql視圖中的緩存的SQL信息。這個視圖包含存儲在共享池中的SQL語句相關的信息,雖然邏輯讀(logical read)最多或者時間消耗(elapsed time)最大的SQL語句通常都是優化的好目標,然而只有仔細觀察每一個步驟才能找準最佳優化時機。在10g中,我們可以使用緩存的查詢計劃的統計信息來定位sql執行中可能需要特別注意的步驟,如視圖v$sql_plan展示所有緩存的sql語句的執行計劃,而v$sql_plan_statistics則展示計劃中每個步驟的執行次數,IO次數以及處理得記錄數。
1.通過挖掘v$sql,確定具有較高消耗時間,CPU或IO需求的SQL語句。通過使用v$sql_plan和v$sql_plan_statistics,我們可以找到執行操作不盡人意的的SQL語句。
a.select sql_id, child_number, sql_text, elapsed_time
from (select sql_id,
child_number,
sql_text,
elapsed_time,
cpu_time,
disk_reads,
rank() over(order by elapsed_time desc) as elapsed_rank
from v$sql)
where elapsed_rank <= 10;
b.DBMS_XPLAN.DISPLAY_CURSOR()看執行計劃
SQL> SELECT * FROM TABLE(DBMS_XPLAN.display_cursor('6axh886a13hhn','0'));
PLAN_TABLE_OUTPUT
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID 6axh886a13hhn, child number 0
-------------------------------------
select NVL(LINE,'NONE') LINE into :b0 from (select A.DESCRIPTION LINE
,count(LH.LOTID) from LOTHISTORY LH ,AREA A where
((((((((LH.WORKAREA=A.WORKAREA and A.WORKAREA='EOL') and
LH.WORKAREA='EOL') and A.LEVELNO=2) and A.DETAILAREATYPE='Normal') and
LH.OLDLINEID=A.AREAID) and LH.LOTID=trim(:b1)) and
LH.EVENTID='TrackOut') and LH.OLDOPERATIONID in ('BFOP001')) group by
A.DESCRIPTION order by count(LOTID) desc ) where ROWNUM=1
Plan hash value: 4058338800
PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 343 (100)| |
|* 1 | COUNT STOPKEY | | | | | |
| 2 | VIEW | | 1 | 129 | 343 (1)| 00:00:01 |
|* 3 | SORT ORDER BY STOPKEY | | 1 | 73 | 343 (1)| 00:00:01 |
| 4 | HASH GROUP BY | | 1 | 73 | 343 (1)| 00:00:01 |
| 5 | NESTED LOOPS | | | | | |
| 6 | NESTED LOOPS | | 1 | 73 | 341 (0)| 00:00:01 |
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|* 7 | TABLE ACCESS BY INDEX ROWID| LOTHISTORY | 1 | 41 | 339 (0)| 00:00:01 |
|* 8 | INDEX RANGE SCAN | IDX_LOTHISTORY_LOTID | 466 | | 6 (0)| 00:00:01 |
|* 9 | INDEX RANGE SCAN | AREA_PK | 1 | | 1 (0)| 00:00:01 |
|* 10 | TABLE ACCESS BY INDEX ROWID | AREA | 1 | 32 | 2 (0)| 00:00:01 |
----------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(ROWNUM=1)
3 - filter(ROWNUM=1)
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
7 - filter(("LH"."OLDOPERATIONID"='BFOP001' AND "LH"."EVENTID"='TrackOut' AND "LH"."OLDLINEID"
IS NOT NULL AND "LH"."WORKAREA"='EOL'))
8 - access("LH"."LOTID"=TRIM(:B1))
9 - access("A"."WORKAREA"='EOL' AND "LH"."OLDLINEID"="A"."AREAID")
filter("LH"."WORKAREA"="A"."WORKAREA")
10 - filter(("A"."LEVELNO"=2 AND "A"."DETAILAREATYPE"='Normal'))
40 rows selected.
2.dbms_xplan工具介紹:
與手工查詢執行計劃相比,使用dbms_xplan通常可以獲得更好的結果,因爲它的語法更加簡單,還提供多種有用的輸出格式,並且可以利用緩存的執行計劃信息。
調用dbms_xplan函數最簡單的方法是使用select * from table()語法:
最常用的兩個函數:
function display(table_name varchar2 default 'PLAN_TABLE',
statement_id varchar2 default null,
format varchar2 default 'TYPICAL',
filter_preds varchar2 default null)
return dbms_xplan_type_table
function display_cursor(sql_id varchar2 default null,
cursor_child_no integer default 0,
format varchar2 default 'TYPICAL')
return dbms_xplan_type_table
DISPLAY函數展示了PLAN_TABLE中的執行計劃,而DISPLAY_CURSOR則展示了再v$sql_plan中緩存的執行計劃的信息。
(1).BASIC只展示執行計劃:
SQL> SELECT * FROM TABLE(DBMS_XPLAN.display_cursor('9q26krp7wx6y8','0','BASIC'));
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
EXPLAINED SQL STATEMENT:
------------------------
insert into LOTCARDDEFECT (select D.* ,trim(:b0) SHIFTDAY
,TO_CHAR(SYSTIMESTAMP,'YYYYMMDDHH24MISSFF6') from (select LC.LOTID
,D.REASONCODE ,D.WORKAREA ,D.OPERATIONID ,sum(D.DEFECTQUANTITY) QTY
from PRODUCTSPEC PS ,LOTDEFECTDETAIL D ,LOTCARD LC ,DEFECTCODE DC where
((((((((((((1=1 and PS.WORKAREA=D.WORKAREA) and
PS.PRODUCTSPECID=D.PRODUCTSPECID) and D.LOTID like (LC.LOTID||'%')) and
D.REASONCODE=DC.DEFECTCODE) and D.WORKAREA=DC.WORKAREA) and
PS.WORKAREA<>'EOL') and D.WORKAREA<>'EOL') and PS.PRODUCTFAMILY in
('VERMONT','VERMONT+')) and LC.SHIFTDAY=trim(:b0)) and D.REASONCODE in
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
(select DEFECTCODE from LOTCARDDEFECTHEADER DH where
((DH.HEADERTYPE='ITEM' and DH.WORKAREA<>'EOL') and
DH.REVISION='2014-07-07'))) and D.TESTLEVEL=0) and DC.TESTFLAG<>'Y')
group by LC.LOTID,D.WORKAREA,D.OPERATIONID,D.REASONCODE union select
LC.LOTID ,'ETC' REASONCODE ,D.WORKAREA ,D.OPERATIONID
,sum(D.DEFECTQUANTITY) QTY from PRODUCTSPEC PS ,LOTDEFECTDETAIL D
,LOTCARD LC ,DEFECTCODE DC where ((((((((((((1=1 a
Plan hash value: 964220270
--------------------------------------------------------------------
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name |
--------------------------------------------------------------------
| 0 | INSERT STATEMENT | |
| 1 | LOAD TABLE CONVENTIONAL | |
| 2 | VIEW | |
| 3 | SORT UNIQUE | |
| 4 | UNION-ALL | |
| 5 | HASH GROUP BY | |
| 6 | HASH JOIN | |
| 7 | HASH JOIN | |
| 8 | TABLE ACCESS FULL | PRODUCTSPEC |
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| 9 | HASH JOIN | |
| 10 | TABLE ACCESS FULL | LOTDEFECTDETAIL |
| 11 | MERGE JOIN CARTESIAN | |
| 12 | TABLE ACCESS BY INDEX ROWID| LOTCARD |
| 13 | INDEX RANGE SCAN | LOTCARD_IDX01 |
| 14 | BUFFER SORT | |
| 15 | SORT UNIQUE | |
| 16 | TABLE ACCESS FULL | LOTCARDDEFECTHEADER |
| 17 | TABLE ACCESS FULL | DEFECTCODE |
| 18 | HASH GROUP BY | |
| 19 | HASH JOIN RIGHT ANTI | |
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| 20 | TABLE ACCESS FULL | LOTCARDDEFECTHEADER |
| 21 | HASH JOIN | |
| 22 | TABLE ACCESS FULL | DEFECTCODE |
| 23 | HASH JOIN | |
| 24 | TABLE ACCESS FULL | LOTDEFECTDETAIL |
| 25 | MERGE JOIN CARTESIAN | |
| 26 | TABLE ACCESS BY INDEX ROWID| LOTCARD |
| 27 | INDEX RANGE SCAN | LOTCARD_IDX01 |
| 28 | BUFFER SORT | |
| 29 | TABLE ACCESS FULL | PRODUCTSPEC |
| 30 | HASH GROUP BY | |
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| 31 | HASH JOIN | |
| 32 | TABLE ACCESS FULL | DEFECTCODE |
| 33 | HASH JOIN | |
| 34 | TABLE ACCESS FULL | LOTDEFECTDETAIL |
| 35 | MERGE JOIN CARTESIAN | |
| 36 | TABLE ACCESS BY INDEX ROWID | LOTCARD |
| 37 | INDEX RANGE SCAN | LOTCARD_IDX01 |
| 38 | BUFFER SORT | |
| 39 | TABLE ACCESS FULL | PRODUCTSPEC |
--------------------------------------------------------------------
66 rows selected.
(2).TYPICAL:默認輸出設置
(3).ALL:輸出所有信息
DBMS_XPLAN 輸出例子:
explain plan for select department_name, last_name, job_title
from hr.employees
join hr.departments
using (department_id)
join hr.jobs
using (job_id)
order by department_name, job_title;
SQL> select * from table(dbms_xplan.display(null,null,'TYPICAL'));
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Plan hash value: 1492005721
----------------------------------------------------------------------------------------------
| Id | Operation | Name
| Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT |
| 106 |
6678 | 10 (20)| 00:00:01 |
| 1 | SORT ORDER BY |
| 106 |
6678 | 10 (20)| 00:00:01 |
|* 2 | HASH JOIN |
| 106 |
6678 | 9 (12)| 00:00:01 |
| 3 | MERGE JOIN |
| 107 |
5029 | 6 (17)| 00:00:01 |
| 4 | TABLE ACCESS BY INDEX ROWID| JOBS
| 19 |
513 | 2 (0)| 00:00:01 |
| 5 | INDEX FULL SCAN | JOB_ID_PK |
19 | |
1 (0)| 00:00:01 |
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|* 6 | SORT JOIN |
| 107 |
2140 | 4 (25)| 00:00:01 |
| 7 | TABLE ACCESS FULL | EMPLOYEES |
107 | 2140 |
3 (0)| 00:00:01 |
| 8 | TABLE ACCESS FULL | DEPARTMENTS |
27 | 432 |
3 (0)| 00:00:01 |
----------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("EMPLOYEES"."DEPARTMENT_ID"="DEPARTMENTS"."DEPARTMENT_ID")
6 - access("EMPLOYEES"."JOB_ID"="JOBS"."JOB_ID")
filter("EMPLOYEES"."JOB_ID"="JOBS"."JOB_ID")
22 rows selected.
3.解釋執行計劃:
(1).縮進越多的訪問路徑越先被執行。
(2)縮進到同樣級別的步驟,先執行最上面的那條語句。
4.虛擬索引(Vitual index)是指沒有創建對應的物理實體的索引。虛擬索引的目的,是在不必耗時,耗CPU,耗IO以及消耗大量存儲空間區實際創建的索引的情況下,來判斷一個索引是否能夠對SQL優化起到作用。
SQL> explain plan for select * from sh.sales where amount_sold >1000;
Explained.
SQL> select * from table(dbms_xplan.display(null,null,'TYPICAL'));
PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------
Plan hash value: 1550251865
---------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
---------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | |
404K| 11M|
526 (2)| 00:00:01 |
| |
| 1 | PARTITION RANGE ALL| |
404K| 11M|
526 (2)| 00:00:01 | 1 |
28 |
|* 2 | TABLE ACCESS FULL | SALES | 404K|
11M| 526 (2)| 00:00:01 |
1 | 28 |
---------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------
---------------------------------------------------
2 - filter("AMOUNT_SOLD">1000)
14 rows selected.
建虛擬索引:
SQL> alter session set "_use_nosegment_indexes"=TRUE;
Session altered.
SQL> create index sh.sales_vit on sh.sales(amount_sold) nosegment;
Index created.
SQL> select * from table(dbms_xplan.display(null,null,'TYPICAL'));
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------------------
Plan hash value: 1798903108
----------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
----------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT |
| 404K| 11M| 366
(0)| 00:00:01 | | |
| 1 | TABLE ACCESS BY GLOBAL INDEX ROWID| SALES | 404K| 11M| 366
(0)| 00:00:01 | ROWID | ROWID |
|* 2 | INDEX RANGE SCAN | SALES_VIT | 404K| | 13
(0)| 00:00:01 | | |
----------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------------------
---------------------------------------------------
2 - access("AMOUNT_SOLD">1000)
14 rows selected.
本例中如果建立索引是有用的,優化器估算的成本從526降366,因此在不創建索引的情況下,可以使用虛擬索引來確定優化器是否應該使用索引。