原文發表在http://l4j.cc/2019/04/21/oralce-access-path-introduce/數據庫
本文梳理、概括了在以前工做中常見的一些執行計劃。瞭解ORACLE中有哪些可能的執行計劃,以及什麼狀況下適合哪一種執行計劃是進行SQL優化的基礎。bash
全表掃描首先會讀取表中的全部行,而後過濾掉不知足條件的數據。全表掃描時,數據庫會以此讀取HWM下的全部格式化了的數據塊,此時數據庫一般會作multiblock read來提升性能,單次讀取的數據塊由DB_FILE_MULTIBLOCK_READ_COUNT
參數指定。 如下狀況會作全表掃描:oracle
下面是一個作全表掃描的列子,其執行計劃的關鍵字爲'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 --------------------------------------------------------------------------------------------
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 ROWID
。oop
這是抽樣檢索數據的數據訪問方式。使用SAMPLE關鍵字對錶中數據進行抽樣的時候會使用該執行計劃,在執行計劃中表現爲TABLE ACCESS SAMPLE
關鍵字。它有如下兩種形式:性能
sample_percent的百分比在[0.000001,100) 範圍內。大數據
只有經過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 行。
經過該索引字段查詢結果可能返回多個值的時候,例如>, <, 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 | ------------------------------------------------------------------------
索引全掃描會有序的讀取整個索引,因爲索引是有序的,因此它會消除額外的排序操做。 如下狀況會優化器會考慮執行索引全掃描:
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 --------------------------------------------------------------------------------------
當查詢的列僅僅包含索引列,且不須要排序的時候,優化器會考慮該訪問方式,它會以磁盤存儲位置來讀取,不會保證數據的有序性。
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 ---------------------------------------------------------------------------------
在複合索引中未使用前導列的狀況,而且前導列只存在不多的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 | ---------------------------------------------------------------------
對同一個表,多個索引的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 行。
傳統的B-tree索引,一條索引對應一條數據。Bitmap的一個索引的鍵值一般對應一批rowid。 Bitmap索引適用於擁有較低distinct基數而且不常常修改的數據。Bitmap索引不適用於常常進行DML操做的列,由於一個index key指向不少數據行,當對一個索引的列進行修改時,會鎖住整個索引條目以及對應的數據行。 還須要注意的一點就是Bitmap索引列是能夠包含空值的。
只要從位圖索引中檢索行,就會用到該訪問方式,進行數據行與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索引的單個鍵值來查詢數據的時候會走該訪問方式,在執行計劃中表現爲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索引列上是一個範圍值的時候,如Bitmap Conversion to Rowid
中的例子,它在執行計劃中表現爲BITMAP INDEX RANGE SCAN
關鍵字。
此訪問路徑合併多個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 行。
在進行多表關聯查詢的時候,優化器須要考慮如下內容:
嵌套循環鏈接,能夠理解爲一個嵌套的for循環,經過外部表的每一條數據去匹配內部表的數據:
FOR erow IN outer_table LOOP FOR drow IN inner_table LOOP return match value END LOOP END LOOP
嵌套循環鏈接適用於如下狀況:
通常狀況下,只有在數據集比較小而且關聯條件存在索引的時候會使用該方式。也能夠經過加入如下hints的方式來強制走嵌套訓話鏈接。:
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表,存放在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 行。
這是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語句改寫爲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.簡單視圖合併 對於簡單試圖,視圖合併老是會帶來更好的執行計劃,因此數據庫老是會進行合併而不會考慮成本。 存在如下狀況時不會進行簡單視圖合併:
下面是一個執行了視圖合併的例子:
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操做延遲到鏈接以後。是否進行復雜視圖合併,取決於優化器對合並後性能的評估。拋開成本問題,如下狀況不會進行復雜視圖合併:
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官網的一個例子:
SELECT * FROM sales WHERE time_id >= TO_DATE('2000-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS') AND prod_id = 38;
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)
ALTER INDEX sales_prod_bix MODIFY PARTITION sales_q4_2003 UNUSABLE; ALTER INDEX sales_time_bix MODIFY PARTITION sales_q4_2003 UNUSABLE;
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語句。