ORACLE中的常見執行計劃

原文發表在http://l4j.cc/2019/04/21/oralce-access-path-introduce/數據庫

 本文梳理、概括了在以前工做中常見的一些執行計劃。瞭解ORACLE中有哪些可能的執行計劃,以及什麼狀況下適合哪一種執行計劃是進行SQL優化的基礎。bash

表訪問相關

Full Table Scans

 全表掃描首先會讀取表中的全部行,而後過濾掉不知足條件的數據。全表掃描時,數據庫會以此讀取HWM下的全部格式化了的數據塊,此時數據庫一般會作multiblock read來提升性能,單次讀取的數據塊由DB_FILE_MULTIBLOCK_READ_COUNT參數指定。  如下狀況會作全表掃描:oracle

  1. 查詢列上不存在索引
  2. 在索引列上使用了函數
  3. 執行SELECT COUNT(*)語句,存在索引,可是索引包含空值
  4. 未使用B-TREE索引的前導列。如存在employees(first_name,last_name)的索引,可是查詢條件爲WHERE last_name='KING'。可是優化器有可能選擇index skip scan
  5. 查詢選擇性很低的時候
  6. 統計信息陳舊
  7. 當表很小的時候,包含的數據塊數n小於DB_FILE_MULTIBLOCK_READ_COUNT參數指定的值
  8. 當表具備很高的並行度的時候
  9. 使用了FULL的hints

 下面是一個作全表掃描的列子,其執行計劃的關鍵字爲'TABLE ACCESS FULL':ide

SQL> select owner,table_name from test_env.tb_table_list where owner='AUDSYS';

OWNER                TABLE_NAME
-------------------- ------------------------------
AUDSYS               AUD$UNIFIED

SQL> select * from table(dbms_xplan.display_cursor(null, null, 'basic'));

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------

EXPLAINED SQL STATEMENT:
------------------------
select owner,table_name from test_env.tb_table_list where owner='AUDSYS'

Plan hash value: 1475094007

-------------------------------------------
| Id  | Operation         | Name          |
-------------------------------------------
|   0 | SELECT STATEMENT  |               |
|   1 |  TABLE ACCESS FULL| TB_TABLE_LIST |

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------

Table Access By Rowid

 ROWID是數據在數據庫中存儲位置的內部表示,它是用來定位單個行最快的方式。一般數據庫經過索引檢索數據行或者指定rowid查詢的時候會使用這種訪問方式。函數

SQL> select owner,table_name from test_env.tb_table_list where TABLE_NAME='ACCESS$';

OWNER                TABLE_NAME
-------------------- ------------------------------
SYS                  ACCESS$

SQL> select * from table(dbms_xplan.display_cursor(null, null, 'basic'));

PLAN_TABLE_OUTPUT
-----------------------------------------------------------------------------------------------------

EXPLAINED SQL STATEMENT:
------------------------
select owner,table_name from test_env.tb_table_list where
TABLE_NAME='ACCESS$'

Plan hash value: 3473397811

------------------------------------------------------------------------
| Id  | Operation                           | Name                     |
------------------------------------------------------------------------
|   0 | SELECT STATEMENT                    |                          |

PLAN_TABLE_OUTPUT
-----------------------------------------------------------------------------------------------------

|   1 |  TABLE ACCESS BY INDEX ROWID BATCHED| TB_TABLE_LIST            |
|   2 |   INDEX RANGE SCAN                  | IDX_TB_TABLE_LIST_TBNAME |
------------------------------------------------------------------------

TABLE ACCESS BY INDEX ROWID BATCHED表示數據庫經過索引中獲得的rowid來檢索數據,BATCHED的訪問方式,表示數據庫會從索引中檢索一批ROWID,而後按塊順序訪問行,減小訪問數據塊的次數來提高性能。這是ORACLE 12C的一個新特性。在11g中上面的執行計劃表示爲TABLE ACCESS BY INDEX ROWIDoop

Sample Table Scans

 這是抽樣檢索數據的數據訪問方式。使用SAMPLE關鍵字對錶中數據進行抽樣的時候會使用該執行計劃,在執行計劃中表現爲TABLE ACCESS SAMPLE關鍵字。它有如下兩種形式:性能

  1. SAMPLE (sample_percent) 數據庫讀取表中指定百分比的行數據。
  2. SAMPLE BLOCK (sample_percent) 數據庫會讀取指定百分比的表數據塊。

 sample_percent的百分比在[0.000001,100) 範圍內。大數據

B-tree索引相關

Index Unique Scans

 只有經過CREATE UNIQUE INDEX建立惟一索引,而且在查詢的時候謂詞條件爲等於的時候纔會以該方式訪問數據。惟一約束(unique和primary key),可是建立的非惟一索引是不足以讓查詢走Index Unique Scan的。在執行計劃中的關鍵字是INDEX UNIQUE SCAN優化

SQL> CREATE UNIQUE INDEX TEST_ENV.IDX_TB_TABLE_LIST_SID ON TEST_ENV.TB_TABLE_LIST(SID);

索引已建立。

SQL> SELECT SID,OWNER,TABLE_NAME FROM TEST_ENV.TB_TABLE_LIST WHERE SID='58223719640640AB8C603BC1B15D5C51';

SID                                  OWNER                TABLE_NAME
------------------------------------ -------------------- ------------------------------
58223719640640AB8C603BC1B15D5C51     SYS                  ACCESS$

SQL> select * from table(dbms_xplan.display_cursor(null, null, 'basic'));

PLAN_TABLE_OUTPUT
-----------------------------------------------------------------------------------------------------------------

EXPLAINED SQL STATEMENT:
------------------------
SELECT SID,OWNER,TABLE_NAME FROM TEST_ENV.TB_TABLE_LIST WHERE
SID='58223719640640AB8C603BC1B15D5C51'

Plan hash value: 3115192837

-------------------------------------------------------------
| Id  | Operation                   | Name                  |
-------------------------------------------------------------
|   0 | SELECT STATEMENT            |                       |

PLAN_TABLE_OUTPUT
-----------------------------------------------------------------------------------------------------------------

|   1 |  TABLE ACCESS BY INDEX ROWID| TB_TABLE_LIST         |
|   2 |   INDEX UNIQUE SCAN         | IDX_TB_TABLE_LIST_SID |
-------------------------------------------------------------


已選擇 15 行。

Index Range Scans

 經過該索引字段查詢結果可能返回多個值的時候,例如>, <, and以及對非惟一索引的=,就會以該方式訪問數據,表如今執行計劃上就是INDEX RANGE SCAN關鍵字。spa

SQL> select owner,table_name from test_env.tb_table_list where TABLE_NAME='ACCESS$';

OWNER                TABLE_NAME
-------------------- ------------------------------
SYS                  ACCESS$

SQL> select * from table(dbms_xplan.display_cursor(null, null, 'basic'));

PLAN_TABLE_OUTPUT
-----------------------------------------------------------------------------------------------------

EXPLAINED SQL STATEMENT:
------------------------
select owner,table_name from test_env.tb_table_list where
TABLE_NAME='ACCESS$'

Plan hash value: 3473397811

------------------------------------------------------------------------
| Id  | Operation                           | Name                     |
------------------------------------------------------------------------
|   0 | SELECT STATEMENT                    |                          |

PLAN_TABLE_OUTPUT
-----------------------------------------------------------------------------------------------------

|   1 |  TABLE ACCESS BY INDEX ROWID BATCHED| TB_TABLE_LIST            |
|   2 |   INDEX RANGE SCAN                  | IDX_TB_TABLE_LIST_TBNAME |
------------------------------------------------------------------------

Index Full Scans

 索引全掃描會有序的讀取整個索引,因爲索引是有序的,因此它會消除額外的排序操做。 如下狀況會優化器會考慮執行索引全掃描:

  • 謂詞使用了索引中的列。該列沒必要是索引的前導列
  • 沒有謂詞條件,可是查詢的列爲該索引的列,而且至少有一個列不爲空
  • 查詢包含ORDER BY 語句而且排序字段爲非空且有索引
SQL> select table_name from test_env.tb_table_list order by table_name;
......

TABLE_NAME
------------------------------
XSTREAM$_SERVER_CONNECTION
XSTREAM$_SUBSET_RULES
XSTREAM$_SYSGEN_OBJS
_default_auditing_options_

已選擇 2138 行。
SQL> select * from table(dbms_xplan.display_cursor(null, null, 'basic'));

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------

EXPLAINED SQL STATEMENT:
------------------------
select table_name from test_env.tb_table_list order by table_name

Plan hash value: 2901892796

-----------------------------------------------------
| Id  | Operation        | Name                     |
-----------------------------------------------------
|   0 | SELECT STATEMENT |                          |
|   1 |  INDEX FULL SCAN | IDX_TB_TABLE_LIST_TBNAME |

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------

Index Fast Full Scans

 當查詢的列僅僅包含索引列,且不須要排序的時候,優化器會考慮該訪問方式,它會以磁盤存儲位置來讀取,不會保證數據的有序性。

SQL> select table_name from test_env.tb_table_list;

TABLE_NAME
------------------------------
XSTREAM$_SERVER_CONNECTION
XSTREAM$_SUBSET_RULES
XSTREAM$_SYSGEN_OBJS
_default_auditing_options_

已選擇 2138 行。

SQL> select * from table(dbms_xplan.display_cursor(null, null, 'basic'));

PLAN_TABLE_OUTPUT
---------------------------------------------------------------------------------

EXPLAINED SQL STATEMENT:
------------------------
select table_name from test_env.tb_table_list

Plan hash value: 3670592075

---------------------------------------------------------
| Id  | Operation            | Name                     |
---------------------------------------------------------
|   0 | SELECT STATEMENT     |                          |
|   1 |  INDEX FAST FULL SCAN| IDX_TB_TABLE_LIST_TBNAME |

PLAN_TABLE_OUTPUT
---------------------------------------------------------------------------------

Index Skip Scans

 在複合索引中未使用前導列的狀況,而且前導列只存在不多的distinct value。其在執行計劃中表現爲INDEX SKIP SCAN

SQL> create index test_env.idx_tb_table_list_mul on test_env.tb_table_list(owner,table_name);

索引已建立。
SQL> select /*+ index(t idx_tb_table_list_mul) */sid,owner,table_name,status from test_env.tb_table_list t where table_name='ACCESS$';

SID                                  OWNER                TABLE_NAME                     STATUS
------------------------------------ -------------------- ------------------------------ --------
58223719640640AB8C603BC1B15D5C51     SYS                  ACCESS$                        VALID

SQL> select * from table(dbms_xplan.display_cursor(null, null, 'basic'));

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------

EXPLAINED SQL STATEMENT:
------------------------
select /*+ index(t idx_tb_table_list_mul) */sid,owner,table_name,status
from test_env.tb_table_list t where table_name='ACCESS$'

Plan hash value: 124701251

---------------------------------------------------------------------
| Id  | Operation                           | Name                  |
---------------------------------------------------------------------
|   0 | SELECT STATEMENT                    |                       |

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------

|   1 |  TABLE ACCESS BY INDEX ROWID BATCHED| TB_TABLE_LIST         |
|   2 |   INDEX SKIP SCAN                   | IDX_TB_TABLE_LIST_MUL |
---------------------------------------------------------------------

Index Join Scans

 對同一個表,多個索引的hash join。當查詢結果都是索引中的列,而且存在於不一樣索引中時會考慮使用該數據訪問方式,可是若是訪問表的效率更高則會根據rowid去訪問表。  Index join的成本是很是的,絕大多數狀況下是不會出現這種狀況,經過索引去探測表一般成本更低。

SQL> select owner,table_name from test_env.tb_table_list where ini_trans>=4;

OWNER           TABLE_NAME
--------------- ------------------------------
MDSYS           SDO_DIST_METADATA_TABLE
SYS             AW$AWCREATE
SYS             AW$AWCREATE10G
SYS             AW$AWMD
SYS             AW$AWREPORT
SYS             AW$AWXML
SYS             AW$EXPRESS
SYS             AW_OBJ$
SYS             AW_PROP$
SYS             OBJECT_USAGE
SYS             PS$

OWNER           TABLE_NAME
--------------- ------------------------------
SYS             STREAMS$_APPLY_PROGRESS

已選擇 12 行。

SQL> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(NULL, NULL, 'basic'));

PLAN_TABLE_OUTPUT
-----------------------------------------------------------------------------------------------

EXPLAINED SQL STATEMENT:
------------------------
select owner,table_name from test_env.tb_table_list where ini_trans>=4

Plan hash value: 832528447

---------------------------------------------------------------------
| Id  | Operation                     | Name                        |
---------------------------------------------------------------------
|   0 | SELECT STATEMENT              |                             |
|   1 |  VIEW                         | index$_join$_001            |

PLAN_TABLE_OUTPUT
-----------------------------------------------------------------------------------------------

|   2 |   HASH JOIN                   |                             |
|   3 |    BITMAP CONVERSION TO ROWIDS|                             |
|   4 |     BITMAP INDEX RANGE SCAN   | BIDX_TB_TABLE_LIST_INITRANS |
|   5 |    INDEX FAST FULL SCAN       | IDX_TB_TABLE_LIST_MUL       |
---------------------------------------------------------------------


已選擇 17 行。

Bitmap索引相關

 傳統的B-tree索引,一條索引對應一條數據。Bitmap的一個索引的鍵值一般對應一批rowid。  Bitmap索引適用於擁有較低distinct基數而且不常常修改的數據。Bitmap索引不適用於常常進行DML操做的列,由於一個index key指向不少數據行,當對一個索引的列進行修改時,會鎖住整個索引條目以及對應的數據行。 還須要注意的一點就是Bitmap索引列是能夠包含空值的。

Bitmap Conversion to Rowid

 只要從位圖索引中檢索行,就會用到該訪問方式,進行數據行與bitmap之間的轉換。

SQL> select owner,table_name,status,ini_trans from test_env.tb_table_list where ini_trans>=4;

OWNER           TABLE_NAME                     STATUS      INI_TRANS
--------------- ------------------------------ ---------- ----------
SYS             PS$                            VALID               4
SYS             AW_OBJ$                        VALID               4
SYS             AW_PROP$                       VALID               4
SYS             AW$EXPRESS                     VALID               4
SYS             AW$AWMD                        VALID               4
SYS             AW$AWCREATE                    VALID               4
SYS             AW$AWCREATE10G                 VALID               4
SYS             AW$AWXML                       VALID               4
SYS             AW$AWREPORT                    VALID               4
SYS             OBJECT_USAGE                   VALID              30
SYS             STREAMS$_APPLY_PROGRESS        VALID             120

OWNER           TABLE_NAME                     STATUS      INI_TRANS
--------------- ------------------------------ ---------- ----------
MDSYS           SDO_DIST_METADATA_TABLE        VALID             255

已選擇 12 行。

SQL> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(NULL, NULL, 'basic'));

PLAN_TABLE_OUTPUT
-----------------------------------------------------------------------------------------------

EXPLAINED SQL STATEMENT:
------------------------
select owner,table_name,status,ini_trans from test_env.tb_table_list
where ini_trans>=4

Plan hash value: 3195249317

---------------------------------------------------------------------------
| Id  | Operation                           | Name                        |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT                    |                             |

PLAN_TABLE_OUTPUT
-----------------------------------------------------------------------------------------------

|   1 |  TABLE ACCESS BY INDEX ROWID BATCHED| TB_TABLE_LIST               |
|   2 |   BITMAP CONVERSION TO ROWIDS       |                             |
|   3 |    BITMAP INDEX RANGE SCAN          | BIDX_TB_TABLE_LIST_INITRANS |
---------------------------------------------------------------------------


已選擇 16 行。

Bitmap Index Single Value

 使用bitmap索引的單個鍵值來查詢數據的時候會走該訪問方式,在執行計劃中表現爲BITMAP INDEX SINGLE VALUE關鍵字。

SQL> SELECT OWNER,TABLE_NAME,TABLESPACE_NAME,STATUS FROM TEST_ENV.TB_TABLE_LIST WHERE TABLESPACE_NAME='SYSTEM';

....
OWNER           TABLE_NAME                     TABLESPACE_NAME      STATUS
--------------- ------------------------------ -------------------- ----------
LBACSYS         OLS$USER_LEVELS                SYSTEM               VALID
LBACSYS         OLS$USER_COMPARTMENTS          SYSTEM               VALID
LBACSYS         OLS$USER_GROUPS                SYSTEM               VALID
LBACSYS         OLS$PROFILES                   SYSTEM               VALID
LBACSYS         OLS$DIP_DEBUG                  SYSTEM               VALID
LBACSYS         OLS$DIP_EVENTS                 SYSTEM               VALID
LBACSYS         OLS$AUDIT                      SYSTEM               VALID
LBACSYS         OLS$AUDIT_ACTIONS              SYSTEM               VALID
SYS             DBMS_SQLPATCH_STATE            SYSTEM               VALID
SYS             DBMS_SQLPATCH_FILES            SYSTEM               VALID
SYS             AQ_SRVNTFN_TABLE_1             SYSTEM               VALID

OWNER           TABLE_NAME                     TABLESPACE_NAME      STATUS
--------------- ------------------------------ -------------------- ----------
SYS             REGISTRY$SQLPATCH_RU_INFO      SYSTEM               VALID

已選擇 912 行。
SQL> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(NULL, NULL, 'basic'));

PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------------------------

EXPLAINED SQL STATEMENT:
------------------------
SELECT OWNER,TABLE_NAME,TABLESPACE_NAME,STATUS FROM
TEST_ENV.TB_TABLE_LIST WHERE TABLESPACE_NAME='SYSTEM'

Plan hash value: 4282437356

-----------------------------------------------------------------------------
| Id  | Operation                           | Name                          |
-----------------------------------------------------------------------------
|   0 | SELECT STATEMENT                    |                               |

PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------------------------

|   1 |  TABLE ACCESS BY INDEX ROWID BATCHED| TB_TABLE_LIST                 |
|   2 |   BITMAP CONVERSION TO ROWIDS       |                               |
|   3 |    BITMAP INDEX SINGLE VALUE        | BIDX_TB_TABLE_LIST_TABLESPACE |
-----------------------------------------------------------------------------


已選擇 16 行。

Bitmap Index Range Scans

 當謂詞條件在bitmap索引列上是一個範圍值的時候,如Bitmap Conversion to Rowid中的例子,它在執行計劃中表現爲BITMAP INDEX RANGE SCAN關鍵字。

Bitmap Merge

 此訪問路徑合併多個Bitmap,並返回單個Bitmap做爲結果。在執行計劃中表現爲BITMAP MERGE關鍵字

SQL> select owner,table_name,status,ini_trans from test_env.tb_table_list where ini_trans>=4 and tablespace_name='SYSTEM';

OWNER           TABLE_NAME                     STATUS      INI_TRANS
--------------- ------------------------------ ---------- ----------
SYS             OBJECT_USAGE                   VALID              30

SQL> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(NULL, NULL, 'basic'));

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------

EXPLAINED SQL STATEMENT:
------------------------
select owner,table_name,status,ini_trans from test_env.tb_table_list
where ini_trans>=4 and tablespace_name='SYSTEM'

Plan hash value: 1400915856

-----------------------------------------------------------------------------
| Id  | Operation                           | Name                          |
-----------------------------------------------------------------------------
|   0 | SELECT STATEMENT                    |                               |

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------

|   1 |  TABLE ACCESS BY INDEX ROWID BATCHED| TB_TABLE_LIST                 |
|   2 |   BITMAP CONVERSION TO ROWIDS       |                               |
|   3 |    BITMAP AND                       |                               |
|   4 |     BITMAP INDEX SINGLE VALUE       | BIDX_TB_TABLE_LIST_TABLESPACE |
|   5 |     BITMAP MERGE                    |                               |
|   6 |      BITMAP INDEX RANGE SCAN        | BIDX_TB_TABLE_LIST_INITRANS   |
-----------------------------------------------------------------------------


已選擇 19 行。

錶鏈接相關

 在進行多表關聯查詢的時候,優化器須要考慮如下內容:

  • 數據訪問方式 同單表查詢同樣,優化器必須選擇是全表掃描仍是經過索引來檢索表中數據
  • 關聯方法 即如何對錶進行關聯。可能的關聯方式有,hash join,nested loops,sorted merge。
  • 關聯類型 關聯條件決定了關聯的類型,如inner join,outer join等
  • 關聯的順序 在進行表關聯的時候,優化器須要決定先關聯哪些表,哪一個表做爲驅動表。基本思想就是儘快的過濾掉儘量多的數據。

Nested Loops Joins

 嵌套循環鏈接,能夠理解爲一個嵌套的for循環,經過外部表的每一條數據去匹配內部表的數據:

FOR erow IN outer_table LOOP
  FOR drow IN inner_table LOOP
    return match value
  END LOOP
END LOOP

 嵌套循環鏈接適用於如下狀況:

  • 數據集很小的時候
  • 在FIRST_ROW的優化器模式下關聯大表
  • 關聯條件能夠高效的訪問內部表

 通常狀況下,只有在數據集比較小而且關聯條件存在索引的時候會使用該方式。也能夠經過加入如下hints的方式來強制走嵌套訓話鏈接。:

  • USE_NL_WITH_INDEX(table index) 由優化器決定那個表做爲驅動表,index是可選的,不指定則由優化器決定
  • ORDERED USE_NL(d) 指定內部表
SQL> select /*+ORDERED USE_NL(B) */A.owner,A.table_name,A.column_name from TEST_ENV.TB_COLUMN_LIST A,TEST_ENV.TB_TABLE_LIST B WHERE A.TABLE_NAME=B.TABLE_NAME AND B.TABLE_NAME='ACCESS$';

OWNER           TABLE_NAME                     COLUMN_NAME
--------------- ------------------------------ --------------------
SYS             ACCESS$                        D_OBJ#
SYS             ACCESS$                        ORDER#
SYS             ACCESS$                        COLUMNS
SYS             ACCESS$                        TYPES

SQL> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(NULL, NULL, 'basic'));

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------

EXPLAINED SQL STATEMENT:
------------------------
select /*+ORDERED USE_NL(B) */A.owner,A.table_name,A.column_name from
TEST_ENV.TB_COLUMN_LIST A,TEST_ENV.TB_TABLE_LIST B WHERE
A.TABLE_NAME=B.TABLE_NAME AND B.TABLE_NAME='ACCESS$'

Plan hash value: 1684280275

----------------------------------------------------------------------
| Id  | Operation                            | Name                  |
----------------------------------------------------------------------

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------

|   0 | SELECT STATEMENT                     |                       |
|   1 |  NESTED LOOPS                        |                       |
|   2 |   TABLE ACCESS BY INDEX ROWID BATCHED| TB_COLUMN_LIST        |
|   3 |    INDEX RANGE SCAN                  | TB_COLUMN_LIST_TBNAME |
|   4 |   INDEX RANGE SCAN                   | TB_TABLE_LIST_TBNAME  |
----------------------------------------------------------------------


已選擇 18 行。

Hash Joins

 這是大數據集關聯的常見方式,也是咱們平常見得最多的關聯方式。優化器會選擇兩個數據集中較小的一個表,而後用關聯字段來構建hash表,存放在PGA中(若是PGA大小不夠存放該hash表,則會將部分數據放入到臨時表中),而後,數據庫掃描較大的數據集,探測哈希表以查找知足鏈接條件的數據行。  贊成經過USE_HASH的hints來強制數據庫走HASH鏈接。

SQL> select /*+USE_HASH(A B) */A.owner,A.table_name,A.column_name from TEST_ENV.TB_COLUMN_LIST A,TEST_ENV.TB_TABLE_LIST
B WHERE A.TABLE_NAME=B.TABLE_NAME AND A.OWNER=B.OWNER AND B.TABLE_NAME='ACCESS$';

OWNER           TABLE_NAME                     COLUMN_NAME
--------------- ------------------------------ --------------------
SYS             ACCESS$                        D_OBJ#
SYS             ACCESS$                        ORDER#
SYS             ACCESS$                        COLUMNS
SYS             ACCESS$                        TYPES

SQL> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(NULL, NULL, 'basic'));

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------

EXPLAINED SQL STATEMENT:
------------------------
select /*+USE_HASH(A B) */A.owner,A.table_name,A.column_name from
TEST_ENV.TB_COLUMN_LIST A,TEST_ENV.TB_TABLE_LIST B WHERE
A.TABLE_NAME=B.TABLE_NAME AND A.OWNER=B.OWNER AND B.TABLE_NAME='ACCESS$'

Plan hash value: 209128112

----------------------------------------------------------------------
| Id  | Operation                            | Name                  |
----------------------------------------------------------------------

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------

|   0 | SELECT STATEMENT                     |                       |
|   1 |  HASH JOIN                           |                       |
|   2 |   TABLE ACCESS BY INDEX ROWID BATCHED| TB_TABLE_LIST         |
|   3 |    INDEX RANGE SCAN                  | TB_TABLE_LIST_TBNAME  |
|   4 |   TABLE ACCESS BY INDEX ROWID BATCHED| TB_COLUMN_LIST        |
|   5 |    INDEX RANGE SCAN                  | TB_COLUMN_LIST_TBNAME |
----------------------------------------------------------------------


已選擇 19 行。

Sorted Merge Joins

 這是Nested Loops的一個變種。數據庫會首先執行SORT JOIN操做對兩個數據集進行排序,而後遍歷外部數據集的每一行,去匹配內部數據集,第二次開始後面每次的匹配位置,取決於前一次迭代的匹配。  如下狀況會選擇進行Sorted Merge:

  • 鏈接條件不是等值鏈接,如>,<>
  • 因爲其餘操做須要排序,優化器發現使用排序合併成本更低。
SQL> select A.owner,A.table_name,A.column_name from TEST_ENV.TB_COLUMN_LIST A,TEST_ENV.TB_TABLE_LIST B WHERE A.TABLE_NAME=B.TABLE_NAME AND A.OWNER=B.OWNER AND B.TABLE_NAME='ACCESS$' ORDER BY TABLE_NAME;

.... 

OWNER           TABLE_NAME                     COLUMN_NAME
--------------- ------------------------------ --------------------
SYS             XSTREAM$_SYSGEN_OBJS           SPARE5
SYS             XSTREAM$_SYSGEN_OBJS           SPARE4
SYS             XSTREAM$_SYSGEN_OBJS           SPARE3
SYS             XSTREAM$_SYSGEN_OBJS           SPARE2
SYS             XSTREAM$_SYSGEN_OBJS           SPARE6
SYS             XSTREAM$_SYSGEN_OBJS           OBJECT_TYPE
SYS             XSTREAM$_SYSGEN_OBJS           OBJECT_NAME
SYS             XSTREAM$_SYSGEN_OBJS           OBJECT_OWNER
SYS             XSTREAM$_SYSGEN_OBJS           SERVER_NAME
SYS             XSTREAM$_SYSGEN_OBJS           SPARE1
SYS             _default_auditing_options_     A

已選擇 23104 行。

SQL> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(NULL, NULL, 'basic'));

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------

EXPLAINED SQL STATEMENT:
------------------------
select A.owner,A.table_name,A.column_name from TEST_ENV.TB_COLUMN_LIST
A,TEST_ENV.TB_TABLE_LIST B WHERE A.TABLE_NAME=B.TABLE_NAME AND
A.OWNER=B.OWNER ORDER BY TABLE_NAME

Plan hash value: 3270676555

---------------------------------------------------
| Id  | Operation              | Name             |
---------------------------------------------------

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------

|   0 | SELECT STATEMENT       |                  |
|   1 |  MERGE JOIN            |                  |
|   2 |   SORT JOIN            |                  |
|   3 |    INDEX FAST FULL SCAN| PK_TB_TABLE_LIST |
|   4 |   SORT JOIN            |                  |
|   5 |    TABLE ACCESS FULL   | TB_COLUMN_LIST   |
---------------------------------------------------


已選擇 19 行。

查詢轉換相關

 咱們發送給ORACLE的目標SQL與最終執行的SQL有多是不一樣的,oracle有可能執行一系列查詢轉換的操做(如視圖合併,子查詢展開等)將SQL改寫成語義等價的其餘形式,是否執行查詢轉換取決於ORACLE對轉換後SQL執行效率的評估。

OR擴展

 OR擴展會將OR語句改寫爲UNION-ALL的形式,這樣各個分支各自走索引、分區修剪、錶鏈接等互不干擾。在以前版本中使用的CONCATENATION來執行OR擴展,12.2開始採用了UNION-ALL的方式。

SQL> select A.owner,A.table_name,B.STATUS,A.column_name from TEST_ENV.TB_COLUMN_LIST A,TEST_ENV.TB_TABLE_LIST B 
2 WHERE A.TABLE_NAME=B.TABLE_NAME AND A.OWNER=B.OWNER AND (B.TABLE_NAME='ACCESS$' OR B.OWNER='XDB');

......

OWNER           TABLE_NAME                     STATUS     COLUMN_NAME
--------------- ------------------------------ ---------- --------------------
XDB             XDB_INDEX_DDL_CACHE            VALID      CONSTR_OWNER
SYS             ACCESS$                        VALID      TYPES
SYS             ACCESS$                        VALID      D_OBJ#
SYS             ACCESS$                        VALID      ORDER#
SYS             ACCESS$                        VALID      COLUMNS

已選擇 170 行。

SQL> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(NULL, NULL, 'BASIC'));

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------

EXPLAINED SQL STATEMENT:
------------------------
select A.owner,A.table_name,B.STATUS,A.column_name from
TEST_ENV.TB_COLUMN_LIST A,TEST_ENV.TB_TABLE_LIST B WHERE
A.TABLE_NAME=B.TABLE_NAME AND A.OWNER=B.OWNER AND
(B.TABLE_NAME='ACCESS$' OR B.OWNER='XDB')

Plan hash value: 1155047104

-------------------------------------------------------------------------
| Id  | Operation                               | Name                  |

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------

-------------------------------------------------------------------------
|   0 | SELECT STATEMENT                        |                       |
|   1 |  NESTED LOOPS                           |                       |
|   2 |   NESTED LOOPS                          |                       |
|   3 |    VIEW                                 | VW_JF_SET$ADB40ABC    |
|   4 |     UNION-ALL                           |                       |
|   5 |      TABLE ACCESS BY INDEX ROWID BATCHED| TB_TABLE_LIST         |
|   6 |       INDEX RANGE SCAN                  | PK_TB_TABLE_LIST      |
|   7 |      TABLE ACCESS BY INDEX ROWID BATCHED| TB_TABLE_LIST         |
|   8 |       INDEX RANGE SCAN                  | TB_TABLE_LIST_TBNAME  |
|   9 |    INDEX RANGE SCAN                     | TB_COLUMN_LIST_TBNAME |

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------

|  10 |   TABLE ACCESS BY INDEX ROWID           | TB_COLUMN_LIST        |
-------------------------------------------------------------------------


已選擇 25 行。

視圖合併

 將視圖合併到包含它的查詢塊中。進行試圖合併後優化器能夠考慮更多的鏈接順序、訪問方式和其餘的查詢轉換,以此來獲取更好的性能。視圖合併又分爲簡單視圖合併和複雜視圖合併。 1.簡單視圖合併  對於簡單試圖,視圖合併老是會帶來更好的執行計劃,因此數據庫老是會進行合併而不會考慮成本。 存在如下狀況時不會進行簡單視圖合併:

  • 視圖中包含如下結構時:GROUP BY,DISTINCT,OUTER JOIN, MODEL, CONNECT BY, 集合操做, 聚合
  • 視圖出如今半鏈接或者反鏈接右側時
  • 視圖出如今SELECT中的子查詢
  • 外部查詢塊包含PL/SQL函數

 下面是一個執行了視圖合併的例子:

SQL> select A.owner,A.table_name,V_B.STATUS,A.column_name from TEST_ENV.TB_COLUMN_LIST A,
    (SELECT OWNER,TABLE_NAME,STATUS FROM TEST_ENV.TB_TABLE_LIST) V_B
    WHERE A.TABLE_NAME=V_B.TABLE_NAME AND A.OWNER=V_B.OWNER;

SQL> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(NULL,NULL,'BASIC'))
PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------------------------

EXPLAINED SQL STATEMENT:
------------------------
select A.owner,A.table_name,V_B.STATUS,A.column_name from
TEST_ENV.TB_COLUMN_LIST A, (SELECT OWNER,TABLE_NAME,STATUS FROM
TEST_ENV.TB_TABLE_LIST) V_B WHERE A.TABLE_NAME=V_B.TABLE_NAME AND
A.OWNER=V_B.OWNER

Plan hash value: 4162551876

---------------------------------------------
| Id  | Operation          | Name           |
---------------------------------------------
|   0 | SELECT STATEMENT   |                |
|   1 |  HASH JOIN         |                |
|   2 |   TABLE ACCESS FULL| TB_TABLE_LIST  |
|   3 |   TABLE ACCESS FULL| TB_COLUMN_LIST |
---------------------------------------------


已選擇 18 行。

而後加個no_merge的hint取消視圖合併,對比下執行計劃。

SQL>  select A.owner,A.table_name,V_B.STATUS,A.column_name from TEST_ENV.TB_COLUMN_LIST A,
      (SELECT /*+no_merge*/OWNER,TABLE_NAME,STATUS FROM TEST_ENV.TB_TABLE_LIST) V_B 
      WHERE A.TABLE_NAME=V_B.TABLE_NAME AND A.OWNER=V_B.OWNER;

SQL> select * from table(dbms_xplan.display_cursor(null, null, 'basic'));

PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------------------

EXPLAINED SQL STATEMENT:
------------------------
select A.owner,A.table_name,V_B.STATUS,A.column_name from
TEST_ENV.TB_COLUMN_LIST A, (SELECT /*+no_merge*/OWNER,TABLE_NAME,STATUS
FROM TEST_ENV.TB_TABLE_LIST) V_B WHERE A.TABLE_NAME=V_B.TABLE_NAME AND
A.OWNER=V_B.OWNER

Plan hash value: 3859876297

----------------------------------------------
| Id  | Operation           | Name           |

PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------------------

----------------------------------------------
|   0 | SELECT STATEMENT    |                |
|   1 |  HASH JOIN          |                |
|   2 |   VIEW              |                |
|   3 |    TABLE ACCESS FULL| TB_TABLE_LIST  |
|   4 |   TABLE ACCESS FULL | TB_COLUMN_LIST |
----------------------------------------------


已選擇 19 行。

 能夠看到第二個執行計劃出現了view關鍵字,表示會把V_B這個視圖進行單獨的處理。

2.複雜視圖合併  複雜視圖合併會合幷包含GROUP BY和DISTINCT操做的視圖,優化器會把GROUP BY和DISTINCT操做延遲到鏈接以後。是否進行復雜視圖合併,取決於優化器對合並後性能的評估。拋開成本問題,如下狀況不會進行復雜視圖合併:

  • 外部查詢表沒有rowid或者unique column
  • 視圖出如今CONNECT BY查詢中
  • 視圖包含 GROUPING SETS, ROLLUP, 或PIVOT
  • 視圖或者外部查詢包含MODEL
SQL> (SELECT OWNER,TABLE_NAME,COUNT(*) AS CNT_COLS FROM TEST_ENV.TB_COLUMN_LIST GROUP BY OWNER,TABLE_NAME) V_A,
  2  TEST_ENV.TB_TABLE_LIST B
  3  WHERE V_A.TABLE_NAME=B.TABLE_NAME AND V_A.OWNER=B.OWNER AND B.OWNER='XDB';
SQL> select * from table(dbms_xplan.display_cursor(null, null, 'basic'));

PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------

EXPLAINED SQL STATEMENT:
------------------------
SELECT B.OWNER,B.TABLE_NAME,B.TABLESPACE_NAME,B.STATUS,V_A.CNT_COLS
FROM (SELECT A.OWNER,A.TABLE_NAME,COUNT(*) AS CNT_COLS FROM
TEST_ENV.TB_COLUMN_LIST A GROUP BY A.OWNER,A.TABLE_NAME) V_A,
TEST_ENV.TB_TABLE_LIST B WHERE V_A.TABLE_NAME=B.TABLE_NAME AND
V_A.OWNER=B.OWNER AND B.OWNER='XDB'

Plan hash value: 3165065995

------------------------------------------------------------------------

PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------

| Id  | Operation                              | Name                  |
------------------------------------------------------------------------
|   0 | SELECT STATEMENT                       |                       |
|   1 |  HASH GROUP BY                         |                       |
|   2 |   NESTED LOOPS                         |                       |
|   3 |    NESTED LOOPS                        |                       |
|   4 |     TABLE ACCESS BY INDEX ROWID BATCHED| TB_TABLE_LIST         |
|   5 |      INDEX RANGE SCAN                  | PK_TB_TABLE_LIST      |
|   6 |     INDEX RANGE SCAN                   | TB_COLUMN_LIST_TBNAME |
|   7 |    TABLE ACCESS BY INDEX ROWID         | TB_COLUMN_LIST        |
------------------------------------------------------------------------

PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------

已選擇 23 行。

 能夠看到執行計劃中已經沒有VIEW關鍵字了,而且GROUP BY操做延遲到了嵌套循環鏈接以後。

謂詞推入

 優化器處理帶視圖的目標SQL的另外一種優化手段。數據庫能夠將推入的謂詞來訪問索引或者做爲過濾條件,進而走基於索引的嵌套循環鏈接。  仍是上面的列子,如今加一個no_merger的hint,避免作視圖合併。VIEW PUSHED PREDICATE代表此時作的是謂詞推入。

SQL> SELECT B.OWNER,B.TABLE_NAME,B.TABLESPACE_NAME,B.STATUS,V_A.CNT_COLS FROM 
(SELECT /*+no_merge*/A.OWNER,A.TABLE_NAME,COUNT(*) AS CNT_COLS FROM TEST_ENV.TB_COLUMN_LIST A GROUP BY A.OWNER,A.TABLE_NAME) V_A,
TEST_ENV.TB_TABLE_LIST B
WHERE V_A.TABLE_NAME=B.TABLE_NAME AND V_A.OWNER=B.OWNER
AND B.OWNER='XDB';

SQL> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(NULL, NULL, 'BASIC'));

PLAN_TABLE_OUTPUT
---------------------------------------------------------------------------------------

EXPLAINED SQL STATEMENT:
------------------------
SELECT B.OWNER,B.TABLE_NAME,B.TABLESPACE_NAME,B.STATUS,V_A.CNT_COLS
FROM (SELECT /*+no_merge*/A.OWNER,A.TABLE_NAME,COUNT(*) AS CNT_COLS
FROM TEST_ENV.TB_COLUMN_LIST A GROUP BY A.OWNER,A.TABLE_NAME) V_A,
TEST_ENV.TB_TABLE_LIST B WHERE V_A.TABLE_NAME=B.TABLE_NAME AND
V_A.OWNER=B.OWNER AND B.OWNER='XDB'

Plan hash value: 466935718

------------------------------------------------------------------------

PLAN_TABLE_OUTPUT
---------------------------------------------------------------------------------------

| Id  | Operation                              | Name                  |
------------------------------------------------------------------------
|   0 | SELECT STATEMENT                       |                       |
|   1 |  NESTED LOOPS                          |                       |
|   2 |   TABLE ACCESS BY INDEX ROWID BATCHED  | TB_TABLE_LIST         |
|   3 |    INDEX RANGE SCAN                    | PK_TB_TABLE_LIST      |
|   4 |   VIEW PUSHED PREDICATE                |                       |
|   5 |    SORT GROUP BY                       |                       |
|   6 |     TABLE ACCESS BY INDEX ROWID BATCHED| TB_COLUMN_LIST        |
|   7 |      INDEX RANGE SCAN                  | TB_COLUMN_LIST_TBNAME |
------------------------------------------------------------------------

PLAN_TABLE_OUTPUT
---------------------------------------------------------------------------------------



已選擇 23 行。

子查詢展開

 在子查詢展開中優化器會把嵌套的子查詢轉換成一個關聯查詢語句。只有當JOIN語句返回的數據行與原始SQL返回數據行相同,而且子查詢中不包含聚合函數(如AVG)時,優化器纔會執行此轉換。

SQL> SELECT OWNER,TABLE_NAME,COLUMN_NAME FROM TEST_ENV.TB_TABLE_LIST WHERE
  2  (OWNER,TABLE_NAME) IN (SELECT OWNER, TABLE_NAME FROM TEST_ENV.TB_TABLE_LIST);

SQL> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(NULL, NULL, 'BASIC'));

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------

EXPLAINED SQL STATEMENT:
------------------------
SELECT OWNER,TABLE_NAME,COLUMN_NAME FROM TEST_ENV.TB_COLUMN_LIST WHERE
(OWNER,TABLE_NAME) IN (SELECT OWNER, TABLE_NAME FROM
TEST_ENV.TB_TABLE_LIST)

Plan hash value: 2699376560

--------------------------------------------------
| Id  | Operation             | Name             |
--------------------------------------------------

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------

|   0 | SELECT STATEMENT      |                  |
|   1 |  HASH JOIN            |                  |
|   2 |   INDEX FAST FULL SCAN| PK_TB_TABLE_LIST |
|   3 |   TABLE ACCESS FULL   | TB_COLUMN_LIST   |
--------------------------------------------------


已選擇 17 行。

 從上面的執行計劃能夠看出,執行子查詢展開後,表現爲兩個表進行HASH JOIN。咱們可使用no_unnest的hint來讓優化器不對該SQL執行子查詢展開。此時的執行計劃以下:

SQL> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(NULL, NULL, 'BASIC'));

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------

EXPLAINED SQL STATEMENT:
------------------------
select owner,table_name,column_name from test_env.tb_column_list where
(owner,table_name) in(select /*+no_unnest*/ owner,table_name from
test_env.tb_table_list)

Plan hash value: 2714665686

-----------------------------------------------
| Id  | Operation          | Name             |
-----------------------------------------------

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------

|   0 | SELECT STATEMENT   |                  |
|   1 |  FILTER            |                  |
|   2 |   TABLE ACCESS FULL| TB_COLUMN_LIST   |
|   3 |   INDEX UNIQUE SCAN| PK_TB_TABLE_LIST |
-----------------------------------------------


已選擇 17 行。

 不進行子查詢展開後的執行計劃走的是FILTER的類型的過濾了。

表擴展

 基於索引能夠提升查詢的性能,可是維護索引也會產生開銷。在一個大的分區表中(特別是採用業務時間作PARTITION KEY的分區表),常常DML操做比較頻繁的就幾個分區,其餘分區大多數時候都作的全表檢索的時間比較多。在相似這種場景下咱們就能夠只在比較活躍的分區上面建立索引,而其餘分區則不建立。優化器會自動的幫咱們在有索引的分區上面經過索引檢索數據,沒有索引的分區進行全表掃描,而後再把結果進行UNION-ALL操做。  下面是ORACLE官網的一個例子:

  1. 表sales根據time_id進行範圍分區
SELECT * 
FROM   sales 
WHERE  time_id >= TO_DATE('2000-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS') 
AND    prod_id = 38;
  1. 執行該語句獲得的執行計劃以下:
Plan hash value: 3087065703
 
--------------------------------------------------------------------------
|Id| Operation                                  | Name      |Pstart|Pstop|
--------------------------------------------------------------------------
| 0| SELECT STATEMENT                           |              |    |    |
| 1|  PARTITION RANGE ITERATOR                  |              | 13 | 28 |
| 2|   TABLE ACCESS BY LOCAL INDEX ROWID BATCHED| SALES        | 13 | 28 |
| 3|    BITMAP CONVERSION TO ROWIDS             |              |    |    |
|*4|     BITMAP INDEX SINGLE VALUE              |SALES_PROD_BIX| 13 | 28 |
--------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   4 - access("PROD_ID"=38)
  1. 而後將sales_q4_2003分區的索引禁用
ALTER INDEX sales_prod_bix MODIFY PARTITION sales_q4_2003 UNUSABLE;
ALTER INDEX sales_time_bix MODIFY PARTITION sales_q4_2003 UNUSABLE;
  1. 此時數據庫在有索引的分區採用索引檢索數據,對沒有索引的分區進行全表掃描。
Plan hash value: 2120767686
 
---------------------------------------------------------------------------
|Id| Operation                                    | Name     |Pstart|Pstop|
---------------------------------------------------------------------------
| 0| SELECT STATEMENT                             |               |   |   |
| 1|  VIEW                                        | VW_TE_2       |   |   |
| 2|   UNION-ALL                                  |               |   |   |
| 3|    PARTITION RANGE ITERATOR                  |               | 13| 27|
| 4|     TABLE ACCESS BY LOCAL INDEX ROWID BATCHED| SALES         | 13| 27|
| 5|      BITMAP CONVERSION TO ROWIDS             |               |   |   |
|*6|       BITMAP INDEX SINGLE VALUE              | SALES_PROD_BIX| 13| 27|
| 7|    PARTITION RANGE SINGLE                    |               | 28| 28|
|*8|     TABLE ACCESS FULL                        | SALES         | 28| 28|
---------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   6 - access("PROD_ID"=38)
   8 - filter("PROD_ID"=38)

 經過將非活躍分區的索引刪除的方式能夠減小維護索引的成本,優化器會自動的幫咱們進行查詢的改寫,而不影響咱們本來的SQL語句。

相關文章
相關標籤/搜索