專題第一篇《Oracle之SQL優化專題01-查看SQL執行計劃的方法》講到了查看SQL執行計劃的方法,並介紹了各類方法的應用場景,那麼這一篇就主要介紹下如何看懂SQL的執行計劃。畢竟若是SQL的執行計劃都看不懂,那優化就無從談起了。html
關於如何看懂SQL的執行計劃,我把它簡單分爲3個部分:session
<h1 id="1">1.判斷執行計劃的執行順序</h1> **口訣:**先子後父,先上後下。 通常簡單的執行計劃能夠直接根據這個口訣來判斷執行計劃的執行順序。相似的口訣還有"最右最上"之類,其表達的意思都是同樣的,選擇一種本身易接受的口訣記憶便可。 舉一個簡單的例子:ide
SQL> show user USER is "SYS" SQL> alter session set current_schema=scott; SQL> alter session set statistics_level = ALL; SQL> select a.empno, a.ename, b.dname, a.job, a.sal from emp a, dept b 2 where a.deptno = b.deptno and empno = 7788 3 ; EMPNO ENAME DNAME JOB SAL ---------- ---------- -------------- --------- ---------- 7788 SCOTT RESEARCH ANALYST 3000 SQL> select * from table(dbms_xplan.display_cursor(null,null,'allstats last')); PLAN_TABLE_OUTPUT ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ SQL_ID 19ktq5t44xsyp, child number 0 ------------------------------------- select a.empno, a.ename, b.dname, a.job, a.sal from emp a, dept b where a.deptno = b.deptno and empno = 7788 Plan hash value: 2385808155 -------------------------------------------------------------------------------------------------- | Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | -------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.01 | 4 | | 1 | NESTED LOOPS | | 1 | 1 | 1 |00:00:00.01 | 4 | | 2 | TABLE ACCESS BY INDEX ROWID| EMP | 1 | 1 | 1 |00:00:00.01 | 2 | |* 3 | INDEX UNIQUE SCAN | PK_EMP | 1 | 1 | 1 |00:00:00.01 | 1 | | 4 | TABLE ACCESS BY INDEX ROWID| DEPT | 1 | 1 | 1 |00:00:00.01 | 2 | |* 5 | INDEX UNIQUE SCAN | PK_DEPT | 1 | 1 | 1 |00:00:00.01 | 1 | -------------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 3 - access("EMPNO"=7788) 5 - access("A"."DEPTNO"="B"."DEPTNO") 24 rows selected.
執行計劃的執行順序(按Id區分)就是 3 -> 2 -> 5 -> 4 -> 1 -> 0.優化
這裏須要特別注意的是: 1)實際上這個所謂的口訣獲得的執行順序只是爲了方便咱們理解操做數據的順序,而執行計劃實際上是按照Id從上往下遞歸調用的,簡單說其實優化器首先判斷是一條select語句,涉及多表關聯,關聯方式採用了NL Join,而後這個NL Join包含分別對EMP和DEPT索引回表的訪問。this
2)理解了第一點,那對於SQL語句中select部分含有標量子查詢的部分不遵循這個口訣就更好理解了(由於實際這個標量子查詢是最後執行的)。 舉一個簡單例子說明這類標量子查詢:code
SQL> select a.empno, a.ename, b.dname, a.job, a.sal, (select * from dual) x from emp a, dept b 2 where a.deptno = b.deptno and empno = 7788 3 ; EMPNO ENAME DNAME JOB SAL X ---------- ---------- -------------- --------- ---------- - 7788 SCOTT RESEARCH ANALYST 3000 X SQL> select * from table(dbms_xplan.display_cursor(null,null,'allstats last')); PLAN_TABLE_OUTPUT ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ SQL_ID dpbagkc0c6ctv, child number 0 ------------------------------------- select a.empno, a.ename, b.dname, a.job, a.sal, (select * from dual) x from emp a, dept b where a.deptno = b.deptno and empno = 7788 Plan hash value: 1121436124 -------------------------------------------------------------------------------------------------- | 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 | DUAL | 1 | 1 | 1 |00:00:00.01 | 2 | | 2 | NESTED LOOPS | | 1 | 1 | 1 |00:00:00.01 | 4 | | 3 | TABLE ACCESS BY INDEX ROWID| EMP | 1 | 1 | 1 |00:00:00.01 | 2 | |* 4 | INDEX UNIQUE SCAN | PK_EMP | 1 | 1 | 1 |00:00:00.01 | 1 | | 5 | TABLE ACCESS BY INDEX ROWID| DEPT | 1 | 1 | 1 |00:00:00.01 | 2 | |* 6 | INDEX UNIQUE SCAN | PK_DEPT | 1 | 1 | 1 |00:00:00.01 | 1 | -------------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 4 - access("EMPNO"=7788) 6 - access("A"."DEPTNO"="B"."DEPTNO") 25 rows selected.
執行計劃的執行順序(按Id區分),若是按口訣來看就是 1 -> 4 -> 3 -> 6 -> 5 -> 2 -> 0. 而因爲這其中1是標量子查詢,實際要最後執行,因此真實的執行順序爲 4 -> 3 -> 6 -> 5 -> 2 -> 1 -> 0.orm
<h1 id="2">2.理解執行計劃每步的含義</h1> 以上面的執行計劃爲例,要清楚知道每一列表明的含義: - Id 操做id號;htm
Operation 這一列,能夠看到SQL的每一步操做,深刻理解就須要清楚表的主要訪問方式(TABLE ACCESS FULL、TABLE ACCESS BY INDEX ROWID、TABLE ACCESS BY USER ROWID),索引的主要訪問方式(INDEX UNIQUE SCAN、INDEX RANGE SCAN、INDEX FULL SCAN、INDEX FAST FULL SCAN、INDEX SKIP SCAN),多表查詢還須要清楚錶鏈接的方法(NESTED LOOP JOIN、SORT MERGE JOIN、HASH JOIN、MERGE JOIN CARTESIAN)和順序(先訪問驅動表)等知識;對象
Name 這一列,就是操做的具體對象名稱;blog
Starts 這一列,表明訪問次數;好比在NESTED LOOP JOIN中,被驅動表的具體訪問次數就能夠依據Starts的數值來判斷。
E-Rows 預估返回行數;
A-Rows 實際返回行數;
A-Time 實際執行時間;
Buffers 邏輯讀塊/次;
若是是其餘方式,看到的執行計劃內容有所不一樣,好比可能就是下面這樣:
Execution Plan ---------------------------------------------------------- Plan hash value: 1121436124 ---------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ---------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 38 | 2 (0)| 00:00:01 | | 1 | TABLE ACCESS FULL | DUAL | 1 | 2 | 2 (0)| 00:00:01 | | 2 | NESTED LOOPS | | 1 | 38 | 2 (0)| 00:00:01 | | 3 | TABLE ACCESS BY INDEX ROWID| EMP | 1 | 25 | 1 (0)| 00:00:01 | |* 4 | INDEX UNIQUE SCAN | PK_EMP | 1 | | 0 (0)| 00:00:01 | | 5 | TABLE ACCESS BY INDEX ROWID| DEPT | 1 | 13 | 1 (0)| 00:00:01 | |* 6 | INDEX UNIQUE SCAN | PK_DEPT | 1 | | 0 (0)| 00:00:01 | ----------------------------------------------------------------------------------------
能夠看到,前三列沒有區別,後面只有Rows、Bytes、Cost (%CPU)、Time等信息。
<h1 id="3">3.瞭解執行計劃相關的信息</h1> 執行計劃下面還有相關的信息,相似以下:
Predicate Information (identified by operation id): --------------------------------------------------- 4 - access("EMPNO"=7788) 6 - access("A"."DEPTNO"="B"."DEPTNO") Note ----- - SQL plan baseline SQL_PLAN_9sdj6nc4ybu5x2b78d17a used for this statement Statistics ---------------------------------------------------------- 0 recursive calls 0 db block gets 6 consistent gets 0 physical reads 0 redo size 879 bytes sent via SQL*Net to client 520 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 1 rows processed
謂詞信息(Predicate Information) 標識出哪一步有access、filter這樣的操做;
Note部分(Note) 若是SQL執行中用到了動態採樣、基數反饋、自適應遊標共享、SQL_Profile、SPM等,會在這裏顯示出來;
統計信息(Statistics) 這裏指的是SQL執行實際發生的遞歸調用、邏輯讀、物理讀、排序、處理行數等統計信息。