NULL 值與索引
NULL 值與索引
NULL值是關聯式資料庫系統布林型(true,false,unknown)中比較特殊型別的一種值,通常稱為UNKNOWN或空值,即是未知的,不確定的。由於
NULL存在著無數的可能,因此NULL值也不等於NULL值,所以與NULL值相關的操作同樣都為NULL值。正是基於這樣一個特性,對於NULL值列上的B
樹索引導致了is null/is not null不走索引的情形,下面描述了NULL值與索引以及索引NULL列上的執行計劃,如何使得NULL值走索引的情形。
注:本文僅僅討論的是B樹索引上的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能夠被插入。
- 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的其他不同組合情形不再演示,其執行計劃類似。
- 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值與索引(一)中講述了null值與索引的一些基本情況。其主要的內容為,基於允許存在null值的索引列,其索引值不會被儲存;其次
是由於這個特性導致了我們在使用is null時索引失效的情形;最後則是描述的透過為null值列新增not null約束來使得is null走索引。儘管我
們可以透過新增not null來解決is null走索引,當現實中的情況是仍然很多列根本是無法確定的,而必須保持其null特性。對於此種情形該如
何解決呢?
一、透過基於函式的索引來使得is null使用索引
- -->演示環境
- scott@ORCL> select * from v$version where rownum<2;
- BANNER
- ----------------------------------------------------------------
- Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Prod
- -->建立測試表t2
- scott@ORCL> create table t2(obj_id,obj_name) as select object_id,object_name from dba_objects;
- Table created.
- -->演示表t2上不存在not null約束
- scott@ORCL> desc t2
- Name Null? Type
- ----------------------------- -------- --------------------
- OBJ_ID NUMBER
- OBJ_NAME VARCHAR2(128)
- -->為表t2建立一個普通的B樹索引
- scott@ORCL> create index i_t2_obj_id on t2(obj_id);
- Index created.
- -->將表t2列obj_id<=100的obj_id置空
- -->注:在Oracle 10g中空字串等同於null值
- scott@ORCL> update t2 set obj_id='' where obj_id<=100;
- 99 rows updated.
- -->下面的查詢亦表明在此時空字串等同於null值
- scott@ORCL> set null unknown
- scott@ORCL> select * from t2 where obj_id is null and rownum<3;
- OBJ_ID OBJ_NAME
- ---------- ------------------------------
- unknown ICOL$
- unknown I_USER1
- -->收集統計資訊
- scott@ORCL> exec dbms_stats.gather_table_stats('SCOTT','T2',cascade=>true);
- PL/SQL procedure successfully completed.
- -->基於null值上使用not null會使用索引掃描,等同於前面 null值與索引(一) 中的描述
- scott@ORCL> select count(*) from t2 where obj_id is not null;
- Execution Plan
- ----------------------------------------------------------
- Plan hash value: 3840858596
- -------------------------------------------------------------------------------------
- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
- -------------------------------------------------------------------------------------
- | 0 | SELECT STATEMENT | | 1 | 5 | 7 (0)| 00:00:01 |
- | 1 | SORT AGGREGATE | | 1 | 5 | | |
- |* 2 | INDEX FAST FULL SCAN| I_T2_OBJ_ID | 11719 | 58595 | 7 (0)| 00:00:01 |
- -------------------------------------------------------------------------------------
- Predicate Information (identified by operation id):
- ---------------------------------------------------
- 2 - filter("OBJ_ID" IS NOT NULL)
- -->列obj_id is null走全表掃描
- scott@ORCL> select count(*) from t2 where obj_id is null;
- Execution Plan
- ----------------------------------------------------------
- Plan hash value: 3321871023
- ---------------------------------------------------------------------------
- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
- ---------------------------------------------------------------------------
- | 0 | SELECT STATEMENT | | 1 | 5 | 13 (0)| 00:00:01 |
- | 1 | SORT AGGREGATE | | 1 | 5 | | |
- |* 2 | TABLE ACCESS FULL| T2 | 1 | 5 | 13 (0)| 00:00:01 |
- ---------------------------------------------------------------------------
- Predicate Information (identified by operation id):
- ---------------------------------------------------
- 2 - filter("OBJ_ID" IS NULL)
- -->建立基於函式的索引來使得is null走索引
- -->下面使用了nvl函式來建立函式索引,即當obj_id為null值時,儲存-1
- scott@ORCL> create index i_fn_t2_obj_id on t2(nvl(obj_id,-1));
- Index created.
- -->收集索引資訊
- scott@ORCL> exec dbms_stats.gather_index_stats('SCOTT','I_FN_T2_OBJ_ID');
- PL/SQL procedure successfully completed.
- -->可以看到下面的執行計劃中剛剛建立的函式索引已經生效I_FN_T2_OBJ_ID
- scott@ORCL> select count(*) from t2 where nvl(obj_id,-1) = -1;
- Execution Plan
- ----------------------------------------------------------
- Plan hash value: 3983750858
- ------------------------------------------------------------------------------------
- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
- ------------------------------------------------------------------------------------
- | 0 | SELECT STATEMENT | | 1 | 5 | 1 (0)| 00:00:01 |
- | 1 | SORT AGGREGATE | | 1 | 5 | | |
- |* 2 | INDEX RANGE SCAN| I_FN_T2_OBJ_ID | 100 | 500 | 1 (0)| 00:00:01 |
- ------------------------------------------------------------------------------------
- Predicate Information (identified by operation id):
- ---------------------------------------------------
- 2 - access(NVL("OBJ_ID",(-1))=(-1))
- -->下面透過新增一個值為-1(可取任意值)的偽列來建立索引
- scott@ORCL> create index i_new_t2_obj_id on t2(obj_id,-1);
- Index created.
- -->收集索引資訊
- scott@ORCL> exec dbms_stats.gather_index_stats('SCOTT','I_NEW_T2_OBJ_ID');
- PL/SQL procedure successfully completed.
- -->從下面的查詢可以看出obj_id is null使用了剛剛建立的索引
- scott@ORCL> select count(*) from t2 where obj_id is null;
- Execution Plan
- ----------------------------------------------------------
- Plan hash value: 801885198
- -------------------------------------------------------------------------------------
- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
- -------------------------------------------------------------------------------------
- | 0 | SELECT STATEMENT | | 1 | 5 | 2 (0)| 00:00:01 |
- | 1 | SORT AGGREGATE | | 1 | 5 | | |
- |* 2 | INDEX RANGE SCAN| I_NEW_T2_OBJ_ID | 99 | 495 | 2 (0)| 00:00:01 |
- -------------------------------------------------------------------------------------
- Predicate Information (identified by operation id):
- ---------------------------------------------------
- 2 - access("OBJ_ID" IS NULL)
- -->檢視剛剛建立的所有索引的相關統計資訊
- scott@ORCL> select index_name,index_type,blevel,leaf_blocks,num_rows,status,distinct_keys
- 2 from user_indexes where table_name='T2';
- INDEX_NAME INDEX_TYPE BLEVEL LEAF_BLOCKS NUM_ROWS STATUS DISTINCT_KEYS
- --------------- ------------------------------ ---------- ----------- ---------- -------- -------------
- I_FN_T2_OBJ_ID FUNCTION-BASED NORMAL 1 26 11719 VALID 11621
- I_NEW_T2_OBJ_ID FUNCTION-BASED NORMAL 1 32 11719 VALID 11621
- I_T2_OBJ_ID NORMAL 1 25 11620 VALID 11620
- -->從上面的結果可知:
- -->普通的B索引(I_T2_OBJ_ID)使用的索引塊最小,因為null值沒有被儲存,NUM_ROWS與DISTINCT_KEYS即是佐證
- -->使用NVL函式建立的索引I_FN_T2_OBJ_ID中如實的反應了null值,即11620 + null值 = 11621
- -->使用偽列建立的索引依然屬於函式索引,其耗用的葉節點塊數最多,因為多出了一個值(-1)來儲存
- -->儘管使用NVL建立的函式佔用的磁碟空間小於使用偽列建立的索引,當在書寫謂詞時需要帶上NVL函式,而偽列索引中謂詞直接使用is null。
- -->由前面的種種事例再次說明NULL值不會被儲存到索引中,因此基於這個特性可以使用decode函式來壓縮索引列。
- -->在實際應用的多數情形中,如表上有列印狀態列is_printed通常為兩種情形,已列印或未列印,假定1表示已列印,而0表示未列印。
- -->通常情況下90%以上的單據都處於已列印狀態,而僅有10%左右的處於未列印。而經常要使用的情形是查詢未列印的單據並重新列印。
- -->基於上述情況,可以使用點陣圖索引來解決,但此處我們討論的是B樹索引,故不考慮該情形(或者說你使用了非企業版Oracle,不支援點陣圖索引)
- -->此處對於這類情形我們可以使用decode函式來解決這個問題
- -->更新表上的列,使之obj_id為1的行佔絕大多數
- scott@ORCL> update t2 set obj_id=1 where obj_id is not null;
- 11620 rows updated.
- -->更新表,使之obj_id為0的行佔少部分
- scott@ORCL> update t2 set obj_id = 0 where obj_id is null;
- 99 rows updated.
- scott@ORCL> commit;
- -->收集統計資訊
- scott@ORCL> exec dbms_stats.gather_table_stats('SCOTT','T2',cascade=>true);
- PL/SQL procedure successfully completed.
- -->表t2上obj_id列的最終分佈
- scott@ORCL> select obj_id,count(*) from t2 group by obj_id;
- OBJ_ID COUNT(*)
- ---------- ----------
- 1 11620
- 0 99
- -->使用decode函式建立索引
- -->注意此處decode的使用,當obj_id非0值時,其值被賦予為null值,由於該null值不會儲存到索引,因此大部分obj_id列值為1的不會被索引
- scott@ORCL> create index i_fn2_t2_obj_id on t2(decode(obj_id,0,0,null));
- Index created.
- -->收集索引上的統計資訊
- scott@ORCL> exec dbms_stats.gather_index_stats('SCOTT','I_FN2_T2_OBJ_ID');
- PL/SQL procedure successfully completed.
- -->檢視新索引的執行計劃
- scott@ORCL> set autot trace exp;
- scott@ORCL> select count(*) from t2 where decode(obj_id,0,0,null) = 0;
- Execution Plan
- ----------------------------------------------------------
- Plan hash value: 1461308992
- -------------------------------------------------------------------------------------
- | 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 RANGE SCAN| I_FN2_T2_OBJ_ID | 98 | 294 | 1 (0)| 00:00:01 |
- -------------------------------------------------------------------------------------
- Predicate Information (identified by operation id):
- ---------------------------------------------------
- 2 - access(DECODE("OBJ_ID",0,0,NULL)=0)
- -->當直接使用obj_id = 0來查詢時使用的是普通的B樹索引
- scott@ORCL> select count(*) from t2 where obj_id = 0;
- Execution Plan
- ----------------------------------------------------------
- Plan hash value: 1804118247
- ---------------------------------------------------------------------------------
- | 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 RANGE SCAN| I_T2_OBJ_ID | 99 | 297 | 1 (0)| 00:00:01 |
- ---------------------------------------------------------------------------------
- Predicate Information (identified by operation id):
- ---------------------------------------------------
- 2 - access("OBJ_ID"=0)
- -->當使用obj_id = 1來查詢時走全表掃描,因為obj_id = 1佔據表90%以上,由CBO特性決定了走全表掃描
- scott@ORCL> select * from t2 where obj_id = 1;
- Execution Plan
- ----------------------------------------------------------
- Plan hash value: 1513984157
- --------------------------------------------------------------------------
- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
- --------------------------------------------------------------------------
- | 0 | SELECT STATEMENT | | 11620 | 249K| 14 (8)| 00:00:01 |
- |* 1 | TABLE ACCESS FULL| T2 | 11620 | 249K| 14 (8)| 00:00:01 |
- --------------------------------------------------------------------------
- Predicate Information (identified by operation id):
- ---------------------------------------------------
- 1 - filter("OBJ_ID"=1)
- -->表t2上所有索引的統計資訊
- scott@ORCL> select index_name,index_type,blevel,leaf_blocks,num_rows,status,distinct_keys
- 2 from user_indexes where table_name='T2';
- INDEX_NAME INDEX_TYPE BLEVEL LEAF_BLOCKS NUM_ROWS STATUS DISTINCT_KEYS
- --------------- ------------------------------ ---------- ----------- ---------- -------- -------------
- I_FN_T2_OBJ_ID FUNCTION-BASED NORMAL 1 40 11719 VALID 2
- I_NEW_T2_OBJ_ID FUNCTION-BASED NORMAL 1 52 11719 VALID 2
- I_FN2_T2_OBJ_ID FUNCTION-BASED NORMAL 0 1 99 VALID 1
- I_T2_OBJ_ID NORMAL 1 40 11719 VALID 2
- -->從上面的結果可知,索引I_FN2_T2_OBJ_ID僅僅儲存了99跳記錄,且DISTINCT_KEYS值為1個,因為所有非0值的全部被置NULL。
- -->以上方法實現了索引壓縮,避免了較大索引維護所需的開銷,同時也提高了查詢效能。
- -->Author : Robinson Cheng
- -->Blog : http://blog.csdn.net/robinson_0612
1、對於用於連線或經常被謂詞使用到的列應儘可能避免NULL值屬性,因為它容易導致索引失效。
2、為需要使用NULL值的列新增預設值(alter table tb modify(col default 'Y'))。
3、如果NULL值不可避免也不能使用預設值,應考慮為該常用列使用nvl函式建立索引,或使用偽列來建立索引以提高查詢效能。
4、對於複合索引應保證索引中至少有一列不為NULL值,還是因為全部列為NULL時不被索引儲存,以保證使用is null是可以使用索引。
5、對於複合索引應保證索引列應使用資料型別長度最小的列來新增not null約束應節省磁碟空間。
About Me
...............................................................................................................................
● 本文轉載自http://blog.csdn.net/leshami/article/details/7438397,樂沙彌大師
● 本文在itpub(http://blog.itpub.net/26736162)、部落格園(http://www.cnblogs.com/lhrbest)和個人微信公眾號(xiaomaimiaolhr)上有同步更新
● 本文itpub地址:http://blog.itpub.net/26736162/abstract/1/
● 本文部落格園地址:http://www.cnblogs.com/lhrbest
● 本文pdf版及小麥苗雲盤地址:http://blog.itpub.net/26736162/viewspace-1624453/
● 資料庫筆試面試題庫及解答:http://blog.itpub.net/26736162/viewspace-2134706/
● QQ群:230161599 微信群:私聊
● 聯絡我請加QQ好友(646634621),註明新增緣由
● 於 2017-06-02 09:00 ~ 2017-06-30 22:00 在魔都完成
● 文章內容來源於小麥苗的學習筆記,部分整理自網路,若有侵權或不當之處還請諒解
● 版權所有,歡迎分享本文,轉載請保留出處
...............................................................................................................................
拿起手機使用微信客戶端掃描下邊的左邊圖片來關注小麥苗的微信公眾號:xiaomaimiaolhr,掃描右邊的二維碼加入小麥苗的QQ群,學習最實用的資料庫技術。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/26736162/viewspace-2141337/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- NULL 值與索引(二)2014-06-27Null索引
- 索引裡的NULL值與排序小記2011-05-05索引Null排序
- NULL與索引2013-12-17Null索引
- 索引與null(一):單列索引2019-03-04索引Null
- 索引與null(二):組合索引2019-06-01索引Null
- MySQL null值欄位是否使用索引的總結2018-11-30MySqlNull索引
- 淺談索引序列之是否可以儲存NULL值?2017-02-23索引Null
- 關於NULL值在索引裡的兩個疑惑2011-12-27Null索引
- 再說索引與Null值對於Hints及執行計劃的影響2009-03-25索引Null
- mysql中null與“空值”的坑2023-04-12MySqlNull
- MySQL裡null與空值的辨析2013-09-19MySqlNull
- 【NULL】Oracle null值介紹2022-03-21NullOracle
- 論壇藉助:最佳化sql,null值如何走索引2016-12-14SQLNull索引
- MySQL中IS NULL、IS NOT NULL、!=不能用索引?胡扯!2019-08-22MySqlNull索引
- 為什麼索引無法使用is null和is not null2020-12-31索引Null
- 不再迷惑,無值和 NULL 值2017-06-21Null
- not null與check is not null2011-07-12Null
- Oracle vs PostgreSQL,研發注意事項(12) - NULL與索引2019-06-21OracleSQLNull索引
- 索引失效系列——說說is null2011-04-25索引Null
- MySQL null值儲存,null效能影響2019-05-23MySqlNull
- [20231024]NULL值在索引的情況.txt2023-10-30Null索引
- TreeSet的null值與元素型別的約束2018-05-02Null型別
- hive中的null值2014-03-27HiveNull
- null(空值)小結2014-10-31Null
- hive NULL值影響2015-05-31HiveNull
- case when遇上null值2009-10-16Null
- NULL列時,如何使得IS NULL或者IS NOT NULL可以使用索引來提高查詢效率2019-06-30Null索引
- mysql探究之null與not null2014-10-17MySqlNull
- 面試題((A)null).fun()——java中null值的強轉2019-07-22面試題NullJava
- SQL中的空值NULL2015-09-14SQLNull
- SQL server中的NULL值2009-02-25SQLServerNull
- 唯一索引,可以在索引列插入多個null嗎2012-09-28索引Null
- undefined與null與?. ??2022-05-20UndefinedNull
- MySQL案例-TIMESTAMP NOT NULL與NULL2017-08-31MySqlNull
- null與index2018-03-03NullIndex
- null與substr2013-01-22Null
- NULL與排序2007-03-09Null排序
- SQL 查詢中的 NULL 值2020-04-05SQLNull