15.表連接原理

表的連接原理


表的連接方式:

1、循環嵌套連接(nested loop join)

2、哈西連接(hash join)

3、排序合併連接(merge join)


一、循環嵌套連接

A B 循環嵌套連接

方法:

1、掃描其中的一個小表,每讀到一條記錄,就根據這條記錄(連接字段)的值到另外一個表中去查找。

2、另外一個表一般是大表,大表的連接字段上有索引,而且索引用的比較好。

3、掃描小表每讀到一個連接字段的指,就到大表上走索引去掃描,以此循環小表,最後的結果合集就是要求查詢的結果。


小表——又叫做驅動表,或者外表

大表——又叫做被驅動表,或者內表

CBO法則下,oracle會自動去選擇驅動表,被驅動表的連接字段要求有索引而且條件要好,如果驅動表過大,也不適合這種方法。


SQL> conn plsql/plsql

Connected.

SQL> set autotrace trace exp

SQL> set linesize 1000

SQL> select h.hrc_descr,o.org_short_name from org_tab o,hrc_tab h where o.hrc_code=h.hrc_code order by 2;


Execution Plan

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

Plan hash value: 566430324


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

| Id  | Operation           | Name    | Rows  | Bytes | Cost (%CPU)| Time     |

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

|   0 | SELECT STATEMENT    |         |     9 |   306 |     8  (25)| 00:00:01 |

|   1 |  SORT ORDER BY      |         |     9 |   306 |     8  (25)| 00:00:01 |

|*  2 |   HASH JOIN         |         |     9 |   306 |     7  (15)| 00:00:01 |

|   3 |    TABLE ACCESS FULL| ORG_TAB |     9 |   198 |     3   (0)| 00:00:01 |

|   4 |    TABLE ACCESS FULL| HRC_TAB |     9 |   108 |     3   (0)| 00:00:01 |

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


Predicate Information (identified by operation id):

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


  2 - access("O"."HRC_CODE"="H"."HRC_CODE")



強制循環嵌套連接


SQL> select /*+ use_nl(o h) */ h.hrc_descr,o.org_short_name from org_tab o,hrc_tab h where o.hrc_code=h.hrc_code order by 2;


Execution Plan

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

Plan hash value: 3315346234


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

| Id  | Operation                     | Name       | Rows  | Bytes | Cost (%CPU)| Time     |

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

|   0 | SELECT STATEMENT              |            |     9 |   306 |    13   (8)| 00:00:01 |

|   1 |  SORT ORDER BY                |            |     9 |   306 |    13   (8)| 00:00:01 |

|   2 |   NESTED LOOPS                |            |     9 |   306 |    12   (0)| 00:00:01 |

|   3 |    TABLE ACCESS FULL          | ORG_TAB    |     9 |   198 |     3   (0)| 00:00:01 |

|   4 |    TABLE ACCESS BY INDEX ROWID| HRC_TAB    |     1 |    12 |     1   (0)| 00:00:01 |

|*  5 |     INDEX UNIQUE SCAN         | PK_HRC_TAB |     1 |       |     0   (0)| 00:00:01 |

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


Predicate Information (identified by operation id):

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


  5 - access("O"."HRC_CODE"="H"."HRC_CODE")


將HRC_TAB的數據加大


SQL> conn plsql/plsql

Connected.

SQL> desc hrc_tab

Name                                      Null?    Type

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

HRC_CODE                                  NOT NULL NUMBER(4)

HRC_DESCR                                 NOT NULL VARCHAR2(20)


SQL> alter table hrc_tab modify hrc_code number(10);


Table altered.


SQL> begin

 2  for i in 1..100000 loop

 3  insert into hrc_tab values(hrc_org_seq.nextval,'111');

 4  end loop;

 5  end;

 6  /


PL/SQL procedure successfully completed.


SQL> commit;


Commit complete.


SQL> select h.hrc_descr,o.org_short_name from org_tab o,hrc_tab h where o.hrc_code=h.hrc_code order by 2;


Execution Plan

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

Plan hash value: 566430324


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

| Id  | Operation           | Name    | Rows  | Bytes | Cost (%CPU)| Time     |

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

|   0 | SELECT STATEMENT    |         |     9 |   306 |     8  (25)| 00:00:01 |

|   1 |  SORT ORDER BY      |         |     9 |   306 |     8  (25)| 00:00:01 |

|*  2 |   HASH JOIN         |         |     9 |   306 |     7  (15)| 00:00:01 |  --爲什麼還是hash join

|   3 |    TABLE ACCESS FULL| ORG_TAB |     9 |   198 |     3   (0)| 00:00:01 |

|   4 |    TABLE ACCESS FULL| HRC_TAB |     9 |   108 |     3   (0)| 00:00:01 |

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


Predicate Information (identified by operation id):

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


  2 - access("O"."HRC_CODE"="H"."HRC_CODE")


因爲性能數據還是舊的。


分析:

SQL> exec dbms_stats.gather_table_stats(user,'hrc_tab',cascade=>true,estimate_percent=>100);


PL/SQL procedure successfully completed.


索引的結構沒有問題,因爲插入數據是一條條插入的。


SQL> analyze index PK_HRC_TAB validate structure;


Index analyzed.


SQL> set autotrace off



SQL> select btree_space,height,pct_used,(del_lf_rows/decode(lf_rows,0,1,lf_rows))*100||'%' as delete_pct from index_stats;


BTREE_SPACE     HEIGHT   PCT_USED DELETE_PCT

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

   1503280                                   2                           100   0%


SQL> set autotrace trace exp

SQL> exec dbms_stats.gather_table_stats(user,'org_tab',cascade=>true,estimate_percent=>100);


PL/SQL procedure successfully completed.


SQL> select h.hrc_descr,o.org_short_name from org_tab o,hrc_tab h where o.hrc_code=h.hrc_code order by 2;


Execution Plan

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

Plan hash value: 3315346234


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

| Id  | Operation                     | Name       | Rows  | Bytes | Cost (%CPU)| Time     |

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

|   0 | SELECT STATEMENT              |            |     9 |   270 |    13   (8)| 00:00:01 |

|   1 |  SORT ORDER BY                |            |     9 |   270 |    13   (8)| 00:00:01 |

|   2 |   NESTED LOOPS                |            |     9 |   270 |    12   (0)| 00:00:01 |

|   3 |    TABLE ACCESS FULL          | ORG_TAB    |     9 |   198 |     3   (0)| 00:00:01 |

|   4 |    TABLE ACCESS BY INDEX ROWID| HRC_TAB    |     1 |     8 |     1   (0)| 00:00:01 |

|*  5 |     INDEX UNIQUE SCAN         | PK_HRC_TAB |     1 |       |     0   (0)| 00:00:01 |

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


Predicate Information (identified by operation id):

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


  5 - access("O"."HRC_CODE"="H"."HRC_CODE")


SQL> select /*+ use_hash(o h) */ h.hrc_descr,o.org_short_name from org_tab o,hrc_tab h where o.hrc_code=h.hrc_code order by 2;


Execution Plan

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

Plan hash value: 566430324


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

| Id  | Operation           | Name    | Rows  | Bytes | Cost (%CPU)| Time     |

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

|   0 | SELECT STATEMENT    |         |     9 |   270 |    64  (10)| 00:00:01 |

|   1 |  SORT ORDER BY      |         |     9 |   270 |    64  (10)| 00:00:01 |

|*  2 |   HASH JOIN         |         |     9 |   270 |    63   (8)| 00:00:01 |

|   3 |    TABLE ACCESS FULL| ORG_TAB |     9 |   198 |     3   (0)| 00:00:01 |

|   4 |    TABLE ACCESS FULL| HRC_TAB |   100K|   781K|    58   (6)| 00:00:01 |

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


Predicate Information (identified by operation id):

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


  2 - access("O"."HRC_CODE"="H"."HRC_CODE")


此時,hash join代價比nested loop高。


二、哈西連接方式


hash算法——數據結構中講述的一種算法,需要有一個哈西函數,用哈西函數將集合的值哈西化以後,集合的重複值將會變多,叫做簇值,根據簇值來分簇。


x和y關聯

mod(x,10)  -->  10個簇

mod(y,10)  -->  10個簇

簇間匹配,再簇內匹配。


假設兩個數據集合:

S={1,1,1,3,3,4,4,4,4,5,8,8,8,8,10}

B={0,0,1,1,2,2,3,8,9,9,9,10,11}


hash函數還是mod(x,10),將每個子分區用hash算法分出來,匹配後再進行原值的匹配:


分區                               B0       B1        B2     B3    B4     B5      B6      B7      B8    B9

值                           {0,0,10} {1,1,11}  {2,2}       {3}      null   null    null    null   {8}  {9,9,9}

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

S0{10}        Y

S1{1,1,1}             Y

S2 NULL                          N

S3{3,3}                                 Y

S4{4,4,4,4}                                   N

S5{5}                                                  N

S6 NULL                                                         N

S7 NULL                                                                N

S8{8,8,8,8}                                                                Y

S9 NULL                                                                                N

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

只有4個簇可以匹配


hash join的過程:

1、存放數據到hash區域——PGA區域

將小表(B)的數據,經過hash算法得到哈西簇(bucket),將原值放到相應的哈西簇中。

2、創建位圖向量

將B表的連接字段集合讀取到hash簇,oracle記錄連接鍵的唯一值(原值),構建位圖向量,這裏的位圖向量{0,1,2,3,8,9,10,11}

3、超出內存大小的部分(私有PGA區域耗完)會被移動到磁盤的臨時表空間,任何需要寫入到磁盤的操作都會生成IO的損耗,會影響性能, 所以最理想的情況是在PGA中完成HASH的操作。

4、對散列簇進行排序

爲了能充分的利用內存,儘量存儲更多的散列簇,oracle按照各個散列簇的大小在內存和磁盤中排序。

5、讀取大表數據進行hash運算簇間匹配。如果一致,再進行原值的匹配返回結果。如果沒有匹配上,就將S中沒有匹配到的寫到新的散列簇中。算法是一樣的,也就是說在S中,S2,6,7,9被過濾了,同樣把B中的位圖向量進行過濾,這叫做位圖向量的過濾。


hash區域:

SQL> show parameter hash


NAME                                 TYPE        VALUE

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

hash_area_size                       integer     131072


因爲PGA是自動管理方式,所以這個大小不需要手工調整的。


使用hash join的場合:

1、確認小表存在,驅動表,因爲小表是優先在hash區域構建hash簇的表

2、確認涉及到的表和表的連接鍵都被分析過了。

3、確定用hash join方式了,不需要在連接鍵上創建B樹索引。

4、如果SQL語句中,有where的過濾條件,此時這個字段不是連接字段,還是需要考慮創建B樹索引的。如果過濾條件是連接字段,也要考慮創建B樹索引,可以通過索引的搜索將參與運算結果集變小。

5、如果連接鍵值數值分佈不均勻還是要做直方圖的。

6、PGA不足的話,要調整PGA

7、hash join適合大表和超大表的連接,返回大型的結果集合。


SQL> conn scott/scott

Connected.

SQL> create table tt as select * from all_objects;


Table created.


SQL> create table tt1 as select * from all_objects;


Table created.


SQL> exec dbms_stats.gather_table_stats(user,'tt',estimate_percent=>100);


PL/SQL procedure successfully completed.


SQL> exec dbms_stats.gather_table_stats(user,'tt1',estimate_percent=>100);


PL/SQL procedure successfully completed.


SQL> set autotrace trace exp

SQL> set linesize 1000

SQL> select * from tt,tt1 where tt.object_id=tt1.object_id;


Execution Plan

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

Plan hash value: 2918007165


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

| Id  | Operation          | Name | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |

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

|   0 | SELECT STATEMENT   |      | 40909 |  7590K|       |   686   (2)| 00:00:09 |

|*  1 |  HASH JOIN         |      | 40909 |  7590K|  4280K|   686   (2)| 00:00:09 |

|   2 |   TABLE ACCESS FULL| TT1  | 40910 |  3795K|       |   134   (3)| 00:00:02 |

|   3 |   TABLE ACCESS FULL| TT   | 40909 |  3795K|       |   134   (3)| 00:00:02 |

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


Predicate Information (identified by operation id):

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


  1 - access("TT"."OBJECT_ID"="TT1"."OBJECT_ID")


SQL> create index ind_tt on tt(object_id);


Index created.


SQL> create index ind_tt1 on tt1(object_id);


Index created.


SQL> exec dbms_stats.gather_table_stats(user,'tt1',estimate_percent=>100,cascade=>true);


PL/SQL procedure successfully completed.


SQL> exec dbms_stats.gather_table_stats(user,'tt',estimate_percent=>100,cascade=>true);


PL/SQL procedure successfully completed.


SQL> select * from tt,tt1 where tt.object_id=tt1.object_id;


Execution Plan

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

Plan hash value: 2918007165


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

| Id  | Operation          | Name | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |

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

|   0 | SELECT STATEMENT   |      | 40909 |  7590K|       |   686   (2)| 00:00:09 |

|*  1 |  HASH JOIN         |      | 40909 |  7590K|  4280K|   686   (2)| 00:00:09 |

|   2 |   TABLE ACCESS FULL| TT1  | 40910 |  3795K|       |   134   (3)| 00:00:02 |

|   3 |   TABLE ACCESS FULL| TT   | 40909 |  3795K|       |   134   (3)| 00:00:02 |

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


Predicate Information (identified by operation id):

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


  1 - access("TT"."OBJECT_ID"="TT1"."OBJECT_ID")


此時加一個非連接字段的過濾條件:


SQL> select * from tt,tt1 where tt.object_id=tt1.object_id and tt.object_type='TYPE';


Execution Plan

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

Plan hash value: 3719458023


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

| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |

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

|   0 | SELECT STATEMENT   |      |  1705 |   316K|   268   (3)| 00:00:04 |

|*  1 |  HASH JOIN         |      |  1705 |   316K|   268   (3)| 00:00:04 |

|*  2 |   TABLE ACCESS FULL| TT   |  1705 |   158K|   133   (2)| 00:00:02 |

|   3 |   TABLE ACCESS FULL| TT1  | 40910 |  3795K|   134   (3)| 00:00:02 |

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


Predicate Information (identified by operation id):

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


  1 - access("TT"."OBJECT_ID"="TT1"."OBJECT_ID")

  2 - filter("TT"."OBJECT_TYPE"='TYPE')



SQL> create index ind_tt2 on tt(object_type);


Index created.


SQL> exec dbms_stats.gather_table_stats(user,'tt',estimate_percent=>100,cascade=>true);


PL/SQL procedure successfully completed.


SQL> select * from tt,tt1 where tt.object_id=tt1.object_id and tt.object_type='TYPE';


Execution Plan

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

Plan hash value: 712899355


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

| Id  | Operation                    | Name    | Rows  | Bytes | Cost (%CPU)| Time     |

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

|   0 | SELECT STATEMENT             |         |  1249 |   231K|   179   (3)| 00:00:03 |

|*  1 |  HASH JOIN                   |         |  1249 |   231K|   179   (3)| 00:00:03 |

|   2 |   TABLE ACCESS BY INDEX ROWID| TT      |  1249 |   115K|    44   (0)| 00:00:01 |

|*  3 |    INDEX RANGE SCAN          | IND_TT2 |  1249 |       |     4   (0)| 00:00:01 |

|   4 |   TABLE ACCESS FULL          | TT1     | 40910 |  3795K|   134   (3)| 00:00:02 |

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


Predicate Information (identified by operation id):

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


  1 - access("TT"."OBJECT_ID"="TT1"."OBJECT_ID")

  3 - access("TT"."OBJECT_TYPE"='TYPE')


此時一個過濾條件落在了連接鍵上。


SQL> select * from tt,tt1 where tt.object_id=tt1.object_id and tt.object_id=259;


Execution Plan

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

Plan hash value: 2300561305


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

| Id  | Operation                     | Name    | Rows  | Bytes | Cost (%CPU)| Time     |

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

|   0 | SELECT STATEMENT              |         |     1 |   190 |     4   (0)| 00:00:01 |

|   1 |  MERGE JOIN CARTESIAN         |         |     1 |   190 |     4   (0)| 00:00:01 |

|   2 |   TABLE ACCESS BY INDEX ROWID | TT      |     1 |    95 |     2   (0)| 00:00:01 |

|*  3 |    INDEX RANGE SCAN           | IND_TT  |     1 |       |     1   (0)| 00:00:01 |

|   4 |   BUFFER SORT                 |         |     1 |    95 |     2   (0)| 00:00:01 |

|   5 |    TABLE ACCESS BY INDEX ROWID| TT1     |     1 |    95 |     2   (0)| 00:00:01 |

|*  6 |     INDEX RANGE SCAN          | IND_TT1 |     1 |       |     1   (0)| 00:00:01 |

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


Predicate Information (identified by operation id):

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


  3 - access("TT"."OBJECT_ID"=259)

  6 - access("TT1"."OBJECT_ID"=259)


SQL> drop index ind_tt1;


Index dropped.


SQL> drop index ind_tt;


Index dropped.


SQL> exec dbms_stats.gather_table_stats(user,'tt',estimate_percent=>100,cascade=>true);


PL/SQL procedure successfully completed.


SQL> exec dbms_stats.gather_table_stats(user,'tt1',estimate_percent=>100,cascade=>true);


PL/SQL procedure successfully completed.


SQL> select * from tt,tt1 where tt.object_id=tt1.object_id and tt.object_id=259;


Execution Plan

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

Plan hash value: 3719458023


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

| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |

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

|   0 | SELECT STATEMENT   |      |     1 |   190 |   267   (2)| 00:00:04 |

|*  1 |  HASH JOIN         |      |     1 |   190 |   267   (2)| 00:00:04 |

|*  2 |   TABLE ACCESS FULL| TT   |     1 |    95 |   133   (2)| 00:00:02 |

|*  3 |   TABLE ACCESS FULL| TT1  |     1 |    95 |   133   (2)| 00:00:02 |

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


Predicate Information (identified by operation id):

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


  1 - access("TT"."OBJECT_ID"="TT1"."OBJECT_ID")

  2 - filter("TT"."OBJECT_ID"=259)

  3 - filter("TT1"."OBJECT_ID"=259)


三、sort merge join排序合併連接


連接過程:

1、對連接的每個表作全表掃描或者索引全掃————代價很大

2、對其中一個表或者兩個全表掃的結果排序(如果其中的一個表是全索引掃描的方式是不需要排序的,因爲索引本身是排序的)——代價最大

3、連接的時候將兩個結果集合合併 ——代價不大



SQL> select /*+ use_merge(o h) */ h.hrc_descr,o.org_short_name from org_tab o,hrc_tab h where o.hrc_code=h.hrc_code order by 2;


Execution Plan

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

Plan hash value: 3620910538


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

| Id  | Operation                     | Name       | Rows  | Bytes | Cost (%CPU)| Time     |

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

|   0 | SELECT STATEMENT              |            |     9 |   270 |   394   (3)| 00:00:05 |

|   1 |  SORT ORDER BY                |            |     9 |   270 |   394   (3)| 00:00:05 |

|   2 |   MERGE JOIN                  |            |     9 |   270 |   393   (2)| 00:00:05 |

|   3 |    TABLE ACCESS BY INDEX ROWID| HRC_TAB    |   100K|   781K|   389   (2)| 00:00:05 |

|   4 |     INDEX FULL SCAN           | PK_HRC_TAB |   100K|       |   191   (2)| 00:00:03 |

|*  5 |    SORT JOIN                  |            |     9 |   198 |     4  (25)| 00:00:01 |

|   6 |     TABLE ACCESS FULL         | ORG_TAB    |     9 |   198 |     3   (0)| 00:00:01 |

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


Predicate Information (identified by operation id):

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


  5 - access("O"."HRC_CODE"="H"."HRC_CODE")

      filter("O"."HRC_CODE"="H"."HRC_CODE")


SQL> select h.hrc_descr,o.org_short_name from org_tab o,hrc_tab h where o.hrc_code=h.hrc_code order by 2;


Execution Plan

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

Plan hash value: 3315346234


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

| Id  | Operation                     | Name       | Rows  | Bytes | Cost (%CPU)| Time     |

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

|   0 | SELECT STATEMENT              |            |     9 |   270 |    13   (8)| 00:00:01 |

|   1 |  SORT ORDER BY                |            |     9 |   270 |    13   (8)| 00:00:01 |

|   2 |   NESTED LOOPS                |            |     9 |   270 |    12   (0)| 00:00:01 |

|   3 |    TABLE ACCESS FULL          | ORG_TAB    |     9 |   198 |     3   (0)| 00:00:01 |

|   4 |    TABLE ACCESS BY INDEX ROWID| HRC_TAB    |     1 |     8 |     1   (0)| 00:00:01 |

|*  5 |     INDEX UNIQUE SCAN         | PK_HRC_TAB |     1 |       |     0   (0)| 00:00:01 |

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


Predicate Information (identified by operation id):

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


  5 - access("O"."HRC_CODE"="H"."HRC_CODE")



merge join使用場合:

1、9i開始,因爲其排序和掃描的成本太高,大多被hash join取代。

2、如果源表(或者索引)在存儲的時候已經排過序了,或者兩個表的連接列都有索引且都是索引全掃的過程,此時性能會高於hash join、循環嵌套,出現的機率不大。


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

Hint——hint是oracle中的SQL語法,允許用戶在SQL語句中插入提示(hint),影響SQL的執行方式。


誤區:

1、/*+ index(table_name index_name) */認爲這種做法一定能加快執行。

2、hint只要語法沒錯,一定會生效。


SQL> create table t as select rownum id,object_name from all_objects where rownum<100;


Table created.


SQL> create index t_ind on t(id);


Index created.


SQL> exec dbms_stats.gather_table_stats(user,'t',cascade=>true,estimate_percent=>100);


PL/SQL procedure successfully completed.


SQL> set autotrace trace exp

SQL> set linesize 1000

SQL> select * from t where id=100;


Execution Plan

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

Plan hash value: 1376202287


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

| Id  | Operation                   | Name  | Rows  | Bytes | Cost (%CPU)| Time     |

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

|   0 | SELECT STATEMENT            |       |     1 |    18 |     2   (0)| 00:00:01 |

|   1 |  TABLE ACCESS BY INDEX ROWID| T     |     1 |    18 |     2   (0)| 00:00:01 |

|*  2 |   INDEX RANGE SCAN          | T_IND |     1 |       |     1   (0)| 00:00:01 |

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


Predicate Information (identified by operation id):

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


  2 - access("ID"=100)


SQL> select count(*) from t;


Execution Plan

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

Plan hash value: 2966233522


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

| Id  | Operation          | Name | Rows  | Cost (%CPU)| Time     |

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

|   0 | SELECT STATEMENT   |      |     1 |     3   (0)| 00:00:01 |

|   1 |  SORT AGGREGATE    |      |     1 |            |          |

|   2 |   TABLE ACCESS FULL| T    |    99 |     3   (0)| 00:00:01 |

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


SQL> select /*+ index(t t_ind)*/ count(*) from t;


Execution Plan

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

Plan hash value: 2966233522


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

| Id  | Operation          | Name | Rows  | Cost (%CPU)| Time     |

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

|   0 | SELECT STATEMENT   |      |     1 |     3   (0)| 00:00:01 |

|   1 |  SORT AGGREGATE    |      |     1 |            |          |

|   2 |   TABLE ACCESS FULL| T    |    99 |     3   (0)| 00:00:01 |   --爲什麼hint沒有生效?

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


被忽略的原因:count是聚合函數,聚合函數對於null值是不起作用的。如果強制走索引的話,索引中是不索引NULL值的,NULL肯定不會被計算進去的,需求是計算所有不爲null的行,算出的結果中,如果ID爲null,name也不爲空,此時走索引,忽略掉這種情況。強制走索引回導致錯誤的結果。所以被忽略。


SQL> select count(comm),count(1) from emp;


COUNT(COMM)   COUNT(1)

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

              4                      14


SQL> select /*+ index(t t_ind)*/ count(id) from t;  --需求就是不計算id爲null的值


Execution Plan

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

Plan hash value: 646498162


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

| Id  | Operation        | Name  | Rows  | Bytes | Cost (%CPU)| Time     |

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

|   0 | SELECT STATEMENT |       |     1 |     3 |     1   (0)| 00:00:01 |

|   1 |  SORT AGGREGATE  |       |     1 |     3 |            |          |

|   2 |   INDEX FULL SCAN| T_IND |    99 |   297 |     1   (0)| 00:00:01 |

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


如果此時索引是unique索引,會不會被忽略?


unique索引含義——不索引null值,要求不爲null的鍵值是不重複的。


SQL> drop index t_ind;


Index dropped.


SQL> create unique index t_ind on t(id);


Index created.


SQL> exec dbms_stats.gather_table_stats(user,'t',cascade=>true,estimate_percent=>100);


PL/SQL procedure successfully completed.


SQL> select /*+ index(t t_ind)*/ count(*) from t;  --走索引計算的是不爲null的值,但是需求是所有行只要有一個字段不爲null就要計算的。


Execution Plan

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

Plan hash value: 2966233522


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

| Id  | Operation          | Name | Rows  | Cost (%CPU)| Time     |

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

|   0 | SELECT STATEMENT   |      |     1 |     3   (0)| 00:00:01 |

|   1 |  SORT AGGREGATE    |      |     1 |            |          |

|   2 |   TABLE ACCESS FULL| T    |    99 |     3   (0)| 00:00:01 |  --忽略

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


如果此時的索引是主鍵的話,會不會被忽略?


SQL> drop index t_ind;


Index dropped.


SQL> alter table t add constraint t_pk primary key(id);


Table altered.


SQL> exec dbms_stats.gather_table_stats(user,'t',cascade=>true,estimate_percent=>100);


PL/SQL procedure successfully completed.


SQL> select /*+ index(t t_pk)*/ count(*) from t;


Execution Plan

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

Plan hash value: 56794325


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

| Id  | Operation        | Name | Rows  | Cost (%CPU)| Time     |

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

|   0 | SELECT STATEMENT |      |     1 |     1   (0)| 00:00:01 |

|   1 |  SORT AGGREGATE  |      |     1 |            |          |

|   2 |   INDEX FULL SCAN| T_PK |    99 |     1   (0)| 00:00:01 |

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


--沒有被忽略,主鍵不能爲NULL的,需求所有行只要有一個字段不爲null就要計算的,主鍵不爲NULL,此時計算主鍵的個數和計算行數是一樣的。不會被忽略。


SQL> select count(*) from t;


Execution Plan

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

Plan hash value: 56794325


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

| Id  | Operation        | Name | Rows  | Cost (%CPU)| Time     |

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

|   0 | SELECT STATEMENT |      |     1 |     1   (0)| 00:00:01 |

|   1 |  SORT AGGREGATE  |      |     1 |            |          |

|   2 |   INDEX FULL SCAN| T_PK |    99 |     1   (0)| 00:00:01 |

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


SQL> select /*+ full(t) */ count(*) from t;   --全表掃的代價高


Execution Plan

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

Plan hash value: 2966233522


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

| Id  | Operation          | Name | Rows  | Cost (%CPU)| Time     |

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

|   0 | SELECT STATEMENT   |      |     1 |     3   (0)| 00:00:01 |

|   1 |  SORT AGGREGATE    |      |     1 |            |          |

|   2 |   TABLE ACCESS FULL| T    |    99 |     3   (0)| 00:00:01 |

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


第二種情況:如果表使用了別名,提示中也要用別名,否則會被忽略。


SQL> select /*+ full(t) */ count(*) from t a;


Execution Plan

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

Plan hash value: 56794325


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

| Id  | Operation        | Name | Rows  | Cost (%CPU)| Time     |

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

|   0 | SELECT STATEMENT |      |     1 |     1   (0)| 00:00:01 |

|   1 |  SORT AGGREGATE  |      |     1 |            |          |

|   2 |   INDEX FULL SCAN| T_PK |    99 |     1   (0)| 00:00:01 |

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


SQL> select /*+ full(a) */ count(*) from t a;


Execution Plan

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

Plan hash value: 2966233522


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

| Id  | Operation          | Name | Rows  | Cost (%CPU)| Time     |

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

|   0 | SELECT STATEMENT   |      |     1 |     3   (0)| 00:00:01 |

|   1 |  SORT AGGREGATE    |      |     1 |            |          |

|   2 |   TABLE ACCESS FULL| T    |    99 |     3   (0)| 00:00:01 |

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


總結:

1、如果所強制走索引可能導致錯誤的查詢結果的時候,hint會被忽略。

2、如果使用了別名,hint中沒有用,也會被忽略。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章