NULL 值與索引

    NULL值是關係數據庫系統布爾型(true,false,unknown)中比較特殊類型的一種值,通常稱爲UNKNOWN或空值,即是未知的,不確定的。由於
NULL存在着無數的可能,因此NULL值也不等於NULL值,所以與NULL值相關的操作同樣都爲NULL值。正是基於這樣一個特性,對於NULL值列上的B
樹索引導致了is null/is not null不走索引的情形,下面描述了NULL值與索引以及索引NULL列上的執行計劃,如何使得NULL值走索引的情形。
注:本文僅僅討論的是B樹索引上的NULL值,位圖索引不在此範圍之內。

一、null值與索引的關係

  1. scott@ORCL> create table t1(id number,val varchar2(1));  
  2.   
  3. –>爲表t1創建唯一索引  
  4. scott@ORCL> create unique index i_t1_id on t1(id);  
  5.   
  6. scott@ORCL> insert into t1 select null,‘Y’ from dual;  
  7.   
  8. scott@ORCL> insert into t1 select null,‘N’ from dual;  
  9.   
  10. –>從上面的操作可知,儘管列id上存在唯一索引,但由於null值不等於任一null值,因此能夠成功插入  
  11. scott@ORCL> commit;  
  12.   
  13. –>再次爲表添加唯一複合索引,即基於id列與val列  
  14. scott@ORCL> create unique index i_t1_id_val on t1(id,val);  
  15.   
  16. Index created.  
  17.   
  18. –>插入null,’N’的記錄時失敗,提示違反唯一性約束  
  19. scott@ORCL> insert into t1 select null,‘N’ from dual;  
  20. insert into t1 select null,‘N’ from dual  
  21. *  
  22. ERROR at line 1:  
  23. ORA-00001: unique constraint (SCOTT.I_T1_ID_VAL) violated  
  24.   
  25. –>插入null,’Y’的記錄時同樣失敗,提示違反唯一性約束  
  26. scott@ORCL> insert into t1 select null,‘Y’ from dual;  
  27. insert into t1 select null,‘Y’ from dual  
  28. *  
  29. ERROR at line 1:  
  30. ORA-00001: unique constraint (SCOTT.I_T1_ID_VAL) violated  
  31.   
  32. –>插入兩個null值成功  
  33. scott@ORCL> insert into t1 select null,null from dual;  
  34.   
  35. 1 row created.  
  36.   
  37. scott@ORCL> insert into t1 select null,null from dual;  
  38.   
  39. 1 row created.  
  40.   
  41. scott@ORCL> insert into t1 select null,‘A’ from dual;  
  42.   
  43. 1 row created.  
  44.   
  45. scott@ORCL> commit;  
  46.   
  47. Commit complete.  
  48.   
  49. scott@ORCL> set null unknown;  
  50. scott@ORCL> select * from t1;  
  51.   
  52.         ID VAL  
  53. ———- ——————————  
  54. unknown    Y  
  55. unknown    N  
  56. unknown    unknown  
  57. unknown    unknown  
  58. unknown    A  
  59.   
  60. scott@ORCL> exec dbms_stats.gather_table_stats(‘SCOTT’,‘T1’,cascade=>true);  
  61.            
  62. scott@ORCL> select index_name,index_type,blevel,leaf_blocks,num_rows,status,distinct_keys  
  63.   2  from user_indexes  where table_name=‘T1’;  
  64.   
  65. INDEX_NAME      INDEX_TYPE     BLEVEL LEAF_BLOCKS   NUM_ROWS STATUS   DISTINCT_KEYS  
  66. ————— ———- ———- ———– ———- ——– ————-  
  67. I_T1_ID         NORMAL              0           0          0 VALID                0  
  68. I_T1_ID_VAL     NORMAL              0           1          3 VALID                3  
  69.   
  70. –>從上面的情形可知,  
  71. –>基於單列的唯一索引,可以多次插入null值,但其索引上並不存儲null值。  
  72. –>基於多列的複合索引,儘管全爲null值的行可以多次插入,但不全爲null的重複行則不能被插入(注,非唯一複合索引不存在此限制,此處不演示)。  
  73. –>基於多列的複合索引,對於全爲null值的索引值也不會被存儲。如上面的情形,儘管插入了5條記錄,複合索引中只存儲了3條。  
  74. –>注:對於唯一性約束,null值不等於null值,同樣(null,null)也不等同於(null,null),所以上面的兩次null能夠被插入。  
scott@ORCL> create table t1(id number,val varchar2(1));

-->爲表t1創建唯一索引
scott@ORCL> create unique index i_t1_id on t1(id);

scott@ORCL> insert into t1 select null,'Y' from dual;

scott@ORCL> insert into t1 select null,'N' from dual;

-->從上面的操作可知,儘管列id上存在唯一索引,但由於null值不等於任一null值,因此能夠成功插入
scott@ORCL> commit;

-->再次爲表添加唯一複合索引,即基於id列與val列
scott@ORCL> create unique index i_t1_id_val on t1(id,val);

Index created.

-->插入null,'N'的記錄時失敗,提示違反唯一性約束
scott@ORCL> insert into t1 select null,'N' from dual;
insert into t1 select null,'N' from dual
*
ERROR at line 1:
ORA-00001: unique constraint (SCOTT.I_T1_ID_VAL) violated

-->插入null,'Y'的記錄時同樣失敗,提示違反唯一性約束
scott@ORCL> insert into t1 select null,'Y' from dual;
insert into t1 select null,'Y' from dual
*
ERROR at line 1:
ORA-00001: unique constraint (SCOTT.I_T1_ID_VAL) violated

-->插入兩個null值成功
scott@ORCL> insert into t1 select null,null from dual;

1 row created.

scott@ORCL> insert into t1 select null,null from dual;

1 row created.

scott@ORCL> insert into t1 select null,'A' from dual;

1 row created.

scott@ORCL> commit;

Commit complete.

scott@ORCL> set null unknown;
scott@ORCL> select * from t1;

        ID VAL
---------- ------------------------------
unknown    Y
unknown    N
unknown    unknown
unknown    unknown
unknown    A

scott@ORCL> exec dbms_stats.gather_table_stats('SCOTT','T1',cascade=>true);

scott@ORCL> select index_name,index_type,blevel,leaf_blocks,num_rows,status,distinct_keys
  2  from user_indexes  where table_name='T1';

INDEX_NAME      INDEX_TYPE     BLEVEL LEAF_BLOCKS   NUM_ROWS STATUS   DISTINCT_KEYS
--------------- ---------- ---------- ----------- ---------- -------- -------------
I_T1_ID         NORMAL              0           0          0 VALID                0
I_T1_ID_VAL     NORMAL              0           1          3 VALID                3

-->從上面的情形可知,
-->基於單列的唯一索引,可以多次插入null值,但其索引上並不存儲null值。
-->基於多列的複合索引,儘管全爲null值的行可以多次插入,但不全爲null的重複行則不能被插入(注,非唯一複合索引不存在此限制,此處不演示)。
-->基於多列的複合索引,對於全爲null值的索引值也不會被存儲。如上面的情形,儘管插入了5條記錄,複合索引中只存儲了3條。
-->注:對於唯一性約束,null值不等於null值,同樣(null,null)也不等同於(null,null),所以上面的兩次null能夠被插入。
二、null值與執行計劃
  1. scott@ORCL> set autot trace exp;  
  2. scott@ORCL> select * from t1 where id is null;  
  3.   
  4. Execution Plan  
  5. ———————————————————-  
  6. Plan hash value: 3617692013  
  7.   
  8. ————————————————————————–  
  9. | Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |  
  10. ————————————————————————–  
  11. |   0 | SELECT STATEMENT  |      |     5 |     5 |     3   (0)| 00:00:01 |  
  12. |*  1 |  TABLE ACCESS FULL| T1   |     5 |     5 |     3   (0)| 00:00:01 |  
  13. ————————————————————————–  
  14.   
  15. Predicate Information (identified by operation id):  
  16. —————————————————  
  17.   
  18.    1 - filter(”ID” IS NULL)  
  19.   
  20. –>從上面的測試可知,由於null值是不被存儲的,因此當使用id is null作爲謂詞時,走了全表掃描  
  21.      
  22. scott@ORCL> select * from t1 where id is not null;  
  23.   
  24. Execution Plan  
  25. ———————————————————-  
  26. Plan hash value: 796913935  
  27.   
  28. —————————————————————————————  
  29. | Id  | Operation                   | Name    | Rows  | Bytes | Cost (%CPU)| Time     |  
  30. —————————————————————————————  
  31. |   0 | SELECT STATEMENT            |         |     1 |     1 |     0   (0)| 00:00:01 |  
  32. |   1 |  TABLE ACCESS BY INDEX ROWID| T1      |     1 |     1 |     0   (0)| 00:00:01 |  
  33. |*  2 |   INDEX FULL SCAN           | I_T1_ID |     1 |       |     0   (0)| 00:00:01 |  
  34. —————————————————————————————  
  35.   
  36. Predicate Information (identified by operation id):  
  37. —————————————————  
  38.   
  39.    2 - filter(”ID” IS NOT NULL)  
  40.   
  41. –>從上面的測試可知,儘管當前表上id列上的所有值都爲null,但不排除後續記錄插入的id不爲null的列。  
  42. –>故當使用id is not null作爲謂詞時,此時執行計劃中走了索引全掃描。     
  43.   
  44. –>下面來看看複合索引的情形     
  45. scott@ORCL> select * from t1 where val is null;  
  46.   
  47. Execution Plan  
  48. ———————————————————-  
  49. Plan hash value: 3617692013  
  50.   
  51. ————————————————————————–  
  52. | Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |  
  53. ————————————————————————–  
  54. |   0 | SELECT STATEMENT  |      |     2 |     2 |     3   (0)| 00:00:01 |  
  55. |*  1 |  TABLE ACCESS FULL| T1   |     2 |     2 |     3   (0)| 00:00:01 |  
  56. ————————————————————————–  
  57.   
  58. Predicate Information (identified by operation id):  
  59. —————————————————  
  60.   
  61.    1 - filter(”VAL” IS NULL)  
  62.   
  63. scott@ORCL> select * from t1 where val is not null;  
  64.   
  65. Execution Plan  
  66. ———————————————————-  
  67. Plan hash value: 1931510411  
  68.   
  69. ——————————————————————————–  
  70. | Id  | Operation        | Name        | Rows  | Bytes | Cost (%CPU)| Time     |  
  71. ——————————————————————————–  
  72. |   0 | SELECT STATEMENT |             |     3 |     3 |     1   (0)| 00:00:01 |  
  73. |*  1 |  INDEX FULL SCAN | I_T1_ID_VAL |     3 |     3 |     1   (0)| 00:00:01 |  
  74. ——————————————————————————–  
  75.   
  76. Predicate Information (identified by operation id):  
  77. —————————————————  
  78.   
  79.    1 - filter(”VAL” IS NOT NULL)  
  80.   
  81. –>對於複合唯一索引的情形,當使用單列且非前導列謂詞時,使用is null與 is not null等同於單列唯一索引的情形。  
  82. –>即原理也是一樣的,val is null走全表掃描而val is not null走索引。因爲null值不會被存儲。  
  83.   
  84. –>下面看看兩個列都作爲謂詞的情形     
  85. scott@ORCL> select * from t1 where id is null and val is not null;  
  86.   
  87. Execution Plan  
  88. ———————————————————-  
  89. Plan hash value: 1040510552  
  90.   
  91. ——————————————————————————–  
  92. | Id  | Operation        | Name        | Rows  | Bytes | Cost (%CPU)| Time     |  
  93. ——————————————————————————–  
  94. |   0 | SELECT STATEMENT |             |     3 |     3 |     1   (0)| 00:00:01 |  
  95. |*  1 |  INDEX RANGE SCAN| I_T1_ID_VAL |     3 |     3 |     1   (0)| 00:00:01 |  
  96. ——————————————————————————–  
  97.   
  98. Predicate Information (identified by operation id):  
  99. —————————————————  
  100.   
  101.    1 - access(”ID” IS NULL)  
  102.        filter(”VAL” IS NOT NULL)  
  103.   
  104. –>從上面的測試可知,儘管兩個謂詞列上都存在索引,一個爲單列唯一索引,一個爲複合唯一索引。Oracle 選擇了複合索引I_T1_ID_VAL。      
  105.   
  106. scott@ORCL> select * from t1 where id is not null and val is null;  
  107.   
  108. Execution Plan  
  109. ———————————————————-  
  110. Plan hash value: 796913935  
  111.   
  112. —————————————————————————————  
  113. | Id  | Operation                   | Name    | Rows  | Bytes | Cost (%CPU)| Time     |  
  114. —————————————————————————————  
  115. |   0 | SELECT STATEMENT            |         |     1 |     1 |     0   (0)| 00:00:01 |  
  116. |*  1 |  TABLE ACCESS BY INDEX ROWID| T1      |     1 |     1 |     0   (0)| 00:00:01 |  
  117. |*  2 |   INDEX FULL SCAN           | I_T1_ID |     1 |       |     0   (0)| 00:00:01 |  
  118. —————————————————————————————  
  119.   
  120. Predicate Information (identified by operation id):  
  121. —————————————————  
  122.   
  123.    1 - filter(”VAL” IS NULL)  
  124.    2 - filter(”ID” IS NOT NULL)      
  125.   
  126. –>同樣的情形,謂詞的順序與複合索引定義的順序一樣,只不過第一個謂詞爲id is not null,而第二個謂詞爲val is null。  
  127. –>此時Oracle 選擇了單列唯一索引I_T1_ID  
  128. –>看到此,不知道大家是否已明白,即哪個列爲is not null,則會使用該列上的索引,原因還是那句話,索引不存儲null值。  
  129. –>對於顛倒id列與val列以及id,val列爲null或not null的其他不同組合情形不再演示,其執行計劃類似。  
scott@ORCL> set autot trace exp;
scott@ORCL> select * from t1 where id is null;





Execution Plan ---------------------------------------------------------- Plan hash value: 3617692013 -------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | -------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 5 | 5 | 3 (0)| 00:00:01 | |* 1 | TABLE ACCESS FULL| T1 | 5 | 5 | 3 (0)| 00:00:01 | -------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter("ID" IS NULL) -->從上面的測試可知,由於null值是不被存儲的,因此當使用id is null作爲謂詞時,走了全表掃描 scott@ORCL> select * from t1 where id is not null; Execution Plan ---------------------------------------------------------- Plan hash value: 796913935 --------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 1 | 0 (0)| 00:00:01 | | 1 | TABLE ACCESS BY INDEX ROWID| T1 | 1 | 1 | 0 (0)| 00:00:01 | |* 2 | INDEX FULL SCAN | I_T1_ID | 1 | | 0 (0)| 00:00:01 | --------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - filter("ID" IS NOT NULL) -->從上面的測試可知,儘管當前表上id列上的所有值都爲null,但不排除後續記錄插入的id不爲null的列。 -->故當使用id is not null作爲謂詞時,此時執行計劃中走了索引全掃描。 -->下面來看看複合索引的情形 scott@ORCL> select * from t1 where val is null; Execution Plan ---------------------------------------------------------- Plan hash value: 3617692013 -------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | -------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 2 | 2 | 3 (0)| 00:00:01 | |* 1 | TABLE ACCESS FULL| T1 | 2 | 2 | 3 (0)| 00:00:01 | -------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter("VAL" IS NULL) scott@ORCL> select * from t1 where val is not null; Execution Plan ---------------------------------------------------------- Plan hash value: 1931510411 -------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | -------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 3 | 3 | 1 (0)| 00:00:01 | |* 1 | INDEX FULL SCAN | I_T1_ID_VAL | 3 | 3 | 1 (0)| 00:00:01 | -------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter("VAL" IS NOT NULL) -->對於複合唯一索引的情形,當使用單列且非前導列謂詞時,使用is null與 is not null等同於單列唯一索引的情形。 -->即原理也是一樣的,val is null走全表掃描而val is not null走索引。因爲null值不會被存儲。 -->下面看看兩個列都作爲謂詞的情形 scott@ORCL> select * from t1 where id is null and val is not null; Execution Plan ---------------------------------------------------------- Plan hash value: 1040510552 -------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | -------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 3 | 3 | 1 (0)| 00:00:01 | |* 1 | INDEX RANGE SCAN| I_T1_ID_VAL | 3 | 3 | 1 (0)| 00:00:01 | -------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - access("ID" IS NULL) filter("VAL" IS NOT NULL) -->從上面的測試可知,儘管兩個謂詞列上都存在索引,一個爲單列唯一索引,一個爲複合唯一索引。Oracle 選擇了複合索引I_T1_ID_VAL。 scott@ORCL> select * from t1 where id is not null and val is null; Execution Plan ---------------------------------------------------------- Plan hash value: 796913935 --------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 1 | 0 (0)| 00:00:01 | |* 1 | TABLE ACCESS BY INDEX ROWID| T1 | 1 | 1 | 0 (0)| 00:00:01 | |* 2 | INDEX FULL SCAN | I_T1_ID | 1 | | 0 (0)| 00:00:01 | --------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter("VAL" IS NULL) 2 - filter("ID" IS NOT NULL) -->同樣的情形,謂詞的順序與複合索引定義的順序一樣,只不過第一個謂詞爲id is not null,而第二個謂詞爲val is null。 -->此時Oracle 選擇了單列唯一索引I_T1_ID -->看到此,不知道大家是否已明白,即哪個列爲is not null,則會使用該列上的索引,原因還是那句話,索引不存儲null值。 -->對於顛倒id列與val列以及id,val列爲null或not null的其他不同組合情形不再演示,其執行計劃類似。三、使用is null走索引的情形
  1. scott@ORCL> set autot off;  
  2. –刪除原有表上的null值記錄  
  3. scott@ORCL> delete from t1 where val not in(‘Y’,‘N’or val is null;  
  4.   
  5. rows deleted.  
  6.   
  7. scott@ORCL> update t1 set id=1 where val=‘Y’;  
  8.   
  9. 1 row updated.  
  10.   
  11. scott@ORCL> update t1 set id=2 where val=‘N’;  
  12.   
  13. 1 row updated.  
  14.   
  15. scott@ORCL> commit;  
  16.   
  17. Commit complete.  
  18.   
  19. –>對原有記錄更新後的情形  
  20. scott@ORCL> select * from t1;  
  21.   
  22.         ID VAL  
  23. ———- ——————————  
  24.          1 Y  
  25.          2 N  
  26.   
  27. scott@ORCL> exec dbms_stats.gather_table_stats(‘SCOTT’,‘T1’,cascade=>true);  
  28.   
  29. PL/SQL procedure successfully completed.  
  30.   
  31. –>修改表列id使之具有not null約束的特性  
  32. scott@ORCL> alter table t1 modify(id not null);  
  33.   
  34. Table altered.  
  35.   
  36. scott@ORCL> set autot trace exp;  
  37. scott@ORCL> select * from t1 where id is null;  
  38.   
  39. Execution Plan  
  40. ———————————————————-  
  41. Plan hash value: 3160894736  
  42.   
  43. ——————————————————————————–  
  44. | Id  | Operation        | Name        | Rows  | Bytes | Cost (%CPU)| Time     |  
  45. ——————————————————————————–  
  46. |   0 | SELECT STATEMENT |             |     1 |     5 |     0   (0)|          |  
  47. |*  1 |  FILTER          |             |       |       |            |          |  
  48. |   2 |   INDEX FULL SCAN| I_T1_ID_VAL |     2 |    10 |     1   (0)| 00:00:01 |  
  49. ——————————————————————————–  
  50.   
  51. Predicate Information (identified by operation id):  
  52. —————————————————  
  53.   
  54.    1 - filter(NULL IS NOT NULL)  
  55.   
  56. –>從上面的執行計劃中可知,當表t1列id上具有not null 約束時,此時使用id is null選擇了索引範圍掃描  
  57.   
  58. –>下面來看看列val is null 的情形     
  59. scott@ORCL> select * from t1 where val is null;  
  60.   
  61. Execution Plan  
  62. ———————————————————-  
  63. Plan hash value: 48744011  
  64.   
  65. ————————————————————————————  
  66. | Id  | Operation            | Name        | Rows  | Bytes | Cost (%CPU)| Time     |  
  67. ————————————————————————————  
  68. |   0 | SELECT STATEMENT     |             |     1 |     5 |     2   (0)| 00:00:01 |  
  69. |*  1 |  INDEX FAST FULL SCAN| I_T1_ID_VAL |     1 |     5 |     2   (0)| 00:00:01 |  
  70. ————————————————————————————  
  71.   
  72. Predicate Information (identified by operation id):  
  73. —————————————————  
  74.   
  75.    1 - filter(”VAL” IS NULL)  
  76.   
  77. –>儘管val列上允許null值存在,但由於列id上具有not null 約束,且id列與val列存在複合唯一索引,因此此時選擇了索引快速全掃描  
  78. –>其餘不同組合情形大致相同,不再演示  
  79.   
  80. –>爲表t1新增一條val爲null的記錄  
  81. scott@ORCL> insert into t1 select 3,null from dual;  
  82.   
  83. 1 row created.  
  84.   
  85. scott@ORCL> commit;  
  86.   
  87. Commit complete.  
  88.   
  89. scott@ORCL> exec dbms_stats.gather_table_stats(‘SCOTT’,‘T1’,cascade=>true);  
  90.   
  91. PL/SQL procedure successfully completed.  
  92.   
  93. –>下面的查詢中可以看出儘管只有列id有not null約束,當所有的索引值都被存儲  
  94. scott@ORCL> select index_name,index_type,blevel,leaf_blocks,num_rows,status,distinct_keys  
  95.   2  from user_indexes  where table_name=‘T1’;  
  96.   
  97. INDEX_NAME      INDEX_TYPE     BLEVEL LEAF_BLOCKS   NUM_ROWS STATUS   DISTINCT_KEYS  
  98. ————— ———- ———- ———– ———- ——– ————-  
  99. I_T1_ID         NORMAL              0           1          3 VALID                3  
  100. I_T1_ID_VAL     NORMAL              0           1          3 VALID                3  
  101.   
  102. –>Author : Robinson Cheng  
  103. –>Blog :   http://blog.csdn.net/robinson_0612  
scott@ORCL> set autot off;
--刪除原有表上的null值記錄
scott@ORCL> delete from t1 where val not in('Y','N') or val is null;

3 rows deleted.

scott@ORCL> update t1 set id=1 where val='Y';

1 row updated.

scott@ORCL> update t1 set id=2 where val='N';

1 row updated.

scott@ORCL> commit;

Commit complete.

-->對原有記錄更新後的情形
scott@ORCL> select * from t1;

        ID VAL
---------- ------------------------------
         1 Y
         2 N

scott@ORCL> exec dbms_stats.gather_table_stats('SCOTT','T1',cascade=>true);

PL/SQL procedure successfully completed.

-->修改表列id使之具有not null約束的特性
scott@ORCL> alter table t1 modify(id not null);

Table altered.

scott@ORCL> set autot trace exp;
scott@ORCL> select * from t1 where id is null;





Execution Plan ---------------------------------------------------------- Plan hash value: 3160894736 -------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | -------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 5 | 0 (0)| | |* 1 | FILTER | | | | | | | 2 | INDEX FULL SCAN| I_T1_ID_VAL | 2 | 10 | 1 (0)| 00:00:01 | -------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter(NULL IS NOT NULL) -->從上面的執行計劃中可知,當表t1列id上具有not null 約束時,此時使用id is null選擇了索引範圍掃描 -->下面來看看列val is null 的情形 scott@ORCL> select * from t1 where val is null; Execution Plan ---------------------------------------------------------- Plan hash value: 48744011 ------------------------------------------------------------------------------------ | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------------------ | 0 | SELECT STATEMENT | | 1 | 5 | 2 (0)| 00:00:01 | |* 1 | INDEX FAST FULL SCAN| I_T1_ID_VAL | 1 | 5 | 2 (0)| 00:00:01 | ------------------------------------------------------------------------------------ Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter("VAL" IS NULL) -->儘管val列上允許null值存在,但由於列id上具有not null 約束,且id列與val列存在複合唯一索引,因此此時選擇了索引快速全掃描 -->其餘不同組合情形大致相同,不再演示 -->爲表t1新增一條val爲null的記錄 scott@ORCL> insert into t1 select 3,null from dual; 1 row created. scott@ORCL> commit; Commit complete. scott@ORCL> exec dbms_stats.gather_table_stats('SCOTT','T1',cascade=>true); PL/SQL procedure successfully completed. -->下面的查詢中可以看出儘管只有列id有not null約束,當所有的索引值都被存儲 scott@ORCL> select index_name,index_type,blevel,leaf_blocks,num_rows,status,distinct_keys 2 from user_indexes where table_name='T1'; INDEX_NAME INDEX_TYPE BLEVEL LEAF_BLOCKS NUM_ROWS STATUS DISTINCT_KEYS --------------- ---------- ---------- ----------- ---------- -------- ------------- I_T1_ID NORMAL 0 1 3 VALID 3 I_T1_ID_VAL NORMAL 0 1 3 VALID 3 -->Author : Robinson Cheng -->Blog : http://blog.csdn.net/robinson_0612四、總結
    無論是單列唯一索引或複合唯一索引,對於可以爲null的列或複合null值,Oracle不會爲其存儲索引值。
    故在基於單列創建B樹唯一索引或多列創建B樹複合唯一索引的情形下,
    當列上允許爲null值時
        where子句使用了基於is null的情形,其執行計劃走全表掃描。
        where子句使用了基於is not null的情形,其執行計劃走索引掃描(索引範圍掃描或索引全掃描)。
    當列上不允許爲null值時,存在非null約束
        where子句使用了基於is null的情行,其執行計劃走索引掃描。
        where子句使用了基於is not null的情形,其執行計劃也是走索引掃描。
    注:此在Oracle 10g R2(linux)下的情形,不同的優化器版本可能會有偏差。

五、更多參考

NULL 值與索引(二)

SQL tuning 步驟

高效SQL語句必殺技

父遊標、子游標及共享遊標

綁定變量及其優缺點

dbms_xplan之display_cursor函數的使用

dbms_xplan之display函數的使用

執行計劃中各字段各模塊描述

使用 EXPLAIN PLAN 獲取SQL語句執行計劃

啓用 AUTOTRACE 功能

函數使得索引列失效

Oracle 綁定變量窺探

Oracle 自適應共享遊標

   


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