您好,登錄后才能下訂單哦!
小編給大家分享一下Oracle里的常見執行計劃有哪些,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
與表訪問相關的執行計劃
Oracle數據庫里面與表訪問有關的的兩種方法:全表掃描和ROWID掃描。反應在執行計劃上,與全表掃描對應的執行計劃中的關鍵字是“TABLE ACCESS FULL”,與ROWID掃描對應的執行計劃中的關鍵字是"TABLE ACCESS BY USER ROWID"或“TABLE ACCESS BY INDEX ROWID”.
我們來看一下與表訪問的相關的執行計劃,先執行如下SQL:
SQL> select empno,ename,rowid from emp where ename='TURNER';
EMPNO ENAME ROWID
---------- ---------- ------------------
7844 TURNER AAAVREAAEAAAACXAAJ
SQL> select * from table(dbms_xplan.display_cursor(null,null,'ALL'));
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
SQL_ID3bjd8ps607cau, child number 0
-------------------------------------
select empno,ename,rowid from emp where ename='TURNER'
Plan hash value: 3956160932
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 3 (100)| |
|* 1 | TABLE ACCESS FULL| EMP | 1 | 22 | 3 (0)| 00:00:01 |
從上述顯示內容中可以看出,目標sql的執行計劃走的是對表EMP的全表掃描,全表掃描在執行計劃中對應的關鍵字就是“TABLE ACCESS FULL”
將上述sql改寫成以指定的ROWID的方式執行:
SQL> select empno,ename from emp where rowid='AAAVREAAEAAAACXAAJ';
EMPNO ENAME
---------- ----------
7844 TURNER
SQL> select * from table(dbms_xplan.display_cursor(null,null,'ALL'));
PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------------------------
SQL_ID8n08pmh26ud05, child number 0
-------------------------------------
select empno,ename from emp where rowid='AAAVREAAEAAAACXAAJ'
Plan hash value: 1116584662
-----------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | |1 (100)| |
| 1 | TABLE ACCESS BY USER ROWID| EMP |1 | 22 |1 (0)| 00:00:01 |
從上述顯示內容可以看出,現在該sql的執行計劃走的對表emp的rowid掃描,其對應的執行計劃關鍵字“TABLE ACCESS BY USER ROWID”。
注意,ROWID掃描所對應的的執行計劃的關鍵字還有可能是“TABLE ACCESS BY INDEX ROWID”,這取決于訪問表時的ROWID來源。如果ROWID是來源于用戶手工指定(例如上述指定“select empno,ename from emp where rowid='AAAVREAAEAAAACXAAJ'”),則對應的執行計劃關鍵字是“TABLE ACCESS BY USER ROWID”;如果是ROWID是來源于索引,則對應的執行計劃關鍵字是“TABLE ACCESS BY INDEX ROWID”
表EMP的主鍵是列EMPNO(即列EMPNO上有主鍵索引),我們將目標sql改寫成如下形式后執行:
SQL> select empno,ename from emp where empno=7369;
EMPNO ENAME
---------- ----------
7369 SMITH
SQL> select * from table(dbms_xplan.display_cursor(null,null,'ALL'));
PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------------------------
SQL_ID6yzqcfbz5xz3c, child number 0
-------------------------------------
select empno,ename from emp where empno=7369
Plan hash value: 2949544139
--------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 1 (100)| |
| 1 | TABLE ACCESS BY INDEX ROWID| EMP | 1 | 10 | 1 (0)| 00:00:01 |
PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------------------------
|* 2 | INDEX UNIQUE SCAN | PK_EMP | 1 | | 0 (0)| |
--------------------------------------------------------------------------------------
從上述顯示內容可以看出,此時目標sql的執行計劃在訪問表emp走的是對表emp的rowid掃描,因為這里的rowid是來源于索引PK_emp,所以其對應的執行計劃關鍵字是“TABLE ACCESS BY INDEX ROWID”.
2.與B樹索引相關的執行計劃
常見的與B樹索引訪問相關的方法有:包括索引唯一掃描,索引范圍掃描,索引全掃描,索引快速全掃描和索引跳躍式掃描,反應在執行計劃上,與索引唯一掃描對應的關鍵字“INDEX UNIQUE SCAN”,與索引范圍掃描對應的關鍵字是“INDEX RANGE SCAN”,與索引全掃描對應的關鍵字是“INDEX FULL SCAN”,與索引快速全掃描對應的關鍵字是“INDEX FAST FULL SCAN”,與索引跳躍式掃描對應的關鍵字是“INDEX SKIP SCAN”.
下面來看一下與B樹索引訪問相關的的執行計劃實例。創建一個測試表EMPLOYEE:
SQL> create table employee(gender varchar2(1),employee_id number);
Table created.
SQL> insert into employee values('F','99');
1 row created.
SQL> insert into employee values('F','100');
1 row created.
SQL> insert into employee values('M','101');
1 row created.
SQL> insert into employee values('M','102');
1 row created.
SQL> insert into employee values('M','103');
1 row created.
SQL> insert into employee values('M','104');
1 row created.
SQL> insert into employee values('M','105');
1 row created.
SQL> commit;
Commit complete.
SQL> create unique index idx_uni_emp on employee(employee_id);
Index created.
SQL> select * from employee where employee_id=100;
G EMPLOYEE_ID
- -----------
F 100
SQL> select plan_table_output from table(dbms_xplan.display_cursor(null,null,'ALL'));
PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------------------------
SQL_IDbum8qv24s6tqp, child number 0
-------------------------------------
select * from employee where employee_id=100
Plan hash value: 1887894887
-------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | |1 (100)| |
| 1 | TABLE ACCESS BY INDEX ROWID| EMPLOYEE |1 | 15 |1 (0)| 00:00:01 |
PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------------------------
|* 2 | INDEX UNIQUE SCAN | IDX_UNI_EMP |1 | |0 (0)| |
-------------------------------------------------------------------------------------------
從上述顯示內容可以看出,此sql的執行計劃走的是索引IDX_UNI_EMP的索引唯一掃描,索引唯一掃描在執行計劃中對應的關鍵字就是“INDEX UNIQUE SCAN”.
Drop掉上述唯一索引IDX_UNI_EMP
SQL> drop index idx_uni_emp;
Index dropped.
SQL> create index idx_emp_1 on employee(employee_id);
Index created.
SQL> select * from employee where employee_id=100;
G EMPLOYEE_ID
- -----------
F 100
SQL> select plan_table_output from table(dbms_xplan.display_cursor(null,null,'ALL'));
PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------------------------
SQL_IDbum8qv24s6tqp, child number 0
-------------------------------------
select * from employee where employee_id=100
Plan hash value: 2428325319
-----------------------------------------------------------------------------------------
| Id | Operation | Name| Rows| Bytes | Cost (%CPU)| Time|
-----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT |||| 2 (100)||
| 1 | TABLE ACCESS BY INDEX ROWID| EMPLOYEE| 1 | 15 | 2 (0)| 00:00:01 |
PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------------------------
|* 2 | INDEX RANGE SCAN | IDX_EMP_1 | 1 || 1 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------
從上述顯示內容中可以看出,現在改sql的執行計劃走是對索引idx_emp_1的索引范圍掃描,索引范圍掃描在執行計劃中對應的關鍵字就是“INDEX RANGE SCAN”。
truncate表EMPLOYEE中的數據:
SQL> truncate table employee;
Table truncated.
更新插入10000條記錄:
SQL> begin
2 for i in 1..5000 loop
3 insert into employee values('F',i);
4 end loop;
5 commit;
6 end;
7 /
PL/SQL procedure successfully completed.
SQL> begin
2 for i in 5001..10000 loop
3 insert into employee values('M',i);
4 end loop;
5 commit;
6 end;
7 /
PL/SQL procedure successfully completed.
SQL> select gender,count(*) from employee group by gender;
G COUNT(*)
- ----------
M5000
F5000
對表EMPLOYEE收集一下統計信息:
SQL> exec dbms_stats.gather_table_stats(ownname=>'SCOTT',tabname=>'EMPLOYEE',estimate_percent=>100,cascade=>TRUE,no_invalidate=>false,method_opt=>'FOR ALL COLUMNS SIZE 1');
PL/SQL procedure successfully completed.
SQL> set autotrace traceonly
SQL> show user
USER is "SCOTT"
SQL> select employee_id from employee;
10000 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 2119105728
------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 10000 | 40000 | 7 (0)| 00:00:01 |
| 1 | TABLE ACCESS FULL| EMPLOYEE | 10000 | 40000 | 7 (0)| 00:00:01 |
------------------------------------------------------------------------------
Statistics
----------------------------------------------------------
1 recursive calls
0 db block gets
689 consistent gets
0 physical reads
0 redo size
174308 bytes sent via SQL*Net to client
7850 bytes received via SQL*Net from client
668 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
10000 rows processed
明明上述SQL查詢字段employee_id可以通過掃描索引idx_emp_1得到,但oracle依然選擇了對employee的全表掃描。
此時就算我們使用Hint強制讓oracle掃描索引idx_emp_1,從如下結果可以看到,oracle依然選擇了對表employee的全表掃描(即hint失效了)
SQL> select /* index(employee idx_emp_1)*/employee_id from employee;
10000 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 2119105728
------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 10000 | 40000 | 7 (0)| 00:00:01 |
| 1 | TABLE ACCESS FULL| EMPLOYEE | 10000 | 40000 | 7 (0)| 00:00:01 |
------------------------------------------------------------------------------
Statistics
----------------------------------------------------------
1 recursive calls
0 db block gets
689 consistent gets
0 physical reads
0 redo size
174308 bytes sent via SQL*Net to client
7850 bytes received via SQL*Net from client
668 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
10000 rows processed
會出現上述現象是因為oracle無論如何總會保證目標sql結果的正確性,可能會得到錯誤結果的執行路徑orale是不會考慮的。對于idx_emp_1而言,它是一個單鍵值的B樹索引,索引NULL值不會存儲在其中,那么一旦列employee_id中出現null值(雖然這里實際上并沒有null值),則掃描索引IDX_EMP_1的結果就會漏掉那些employee_id為NULL的值,這也就意味這個如果orale在執行上述sql選擇了掃描索引idx_emp_1,那么執行結果就可能是不準的。這種情況下,oracle當然不會考慮掃描idx_emp_1,即使我們使用Hint。
如果這里我們想讓oracle在執行上述sql掃描索引idx_emp_1,則必須將列employee_id的屬性改成not null。這就相當于告訴oracle,這里列employee_id上不會有null值,你就放心的掃描idx_emp_1吧。
SQL> select employee_id from employee;
10000 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 3918702848
----------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 10000 | 40000 | 7 (0)| 00:00:01 |
| 1 | INDEX FAST FULL SCAN| IDX_EMP_1 | 10000 | 40000 | 7 (0)| 00:00:01 |
----------------------------------------------------------------------------------
從上述顯示內容可以看出,現在sql的執行計劃走的是idx_emp_1的索引快速全掃描,索引快速全掃描在執行計劃中對應的關鍵字就是“INDEX FAST FULL SCAN”
現在我們加上強制索引IDX_EMP_1的hint,再次執行該sql:
SQL> select /*+ index(employee idx_emp_1) */ employee_id from employee;
10000 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 438557521
------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 10000 | 40000 | 20 (0)| 00:00:01 |
| 1 | INDEX FULL SCAN | IDX_EMP_1 | 10000 | 40000 | 20 (0)| 00:00:01 |
------------------------------------------------------------------------------
從上述顯示內容可以看出,現在SQL的執行計劃走的是對索引idx_emp_1的索引全掃描,索引全掃描在執行計劃中對應的關鍵字就是“INDEX FULL SCAN”.
DROP掉單鍵值B樹索引IDX_EMP_1;
SQL> drop index idx_emp_1;
Index dropped.
SQL> create index index_emp_2 on employee(gender,employee_id);
Index created.
SQL> set autot trace
SQL> select * from employee where employee_id=101;
Execution Plan
----------------------------------------------------------
Plan hash value: 2052968723
--------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 6 | 3 (0)| 00:00:01 |
|* 1 | INDEX SKIP SCAN | INDEX_EMP_2 | 1 | 6 | 3 (0)| 00:00:01 |
--------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("EMPLOYEE_ID"=101)
filter("EMPLOYEE_ID"=101)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
6 consistent gets
0 physical reads
0 redo size
600 bytes sent via SQL*Net to client
524 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
從上述顯示內容可以看出,現在sql的執行計劃走的是對索引IDX_EMP_2的索引跳躍式掃描,索引跳躍式掃描在執行計劃中對應的關鍵字就是“INDEX SKIP SCAN”.
以上是“Oracle里的常見執行計劃有哪些”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。