索引與null(一):單列索引
這篇文章主要討論單列索引與null的關係。
一、單列索引
SQL> create table t(a int,b int);
Table created
SQL> create index idx_t on t(a);
Index created
SQL> select * from t;
A B
---------- ----------
1 1
2 2
3
SQL> analyze index idx_t compute statistics;
Index analyzed
1、單列是否索引null
SQL> select index_name,NUM_ROWS,DISTINCT_KEYS from user_indexes where index_name='IDX_T';
INDEX_NAME NUM_ROWS DISTINCT_KEYS
------------------------------ ---------- -------------
IDX_T 2 2
從以上可以看出,索引不索引null值,只索引了a=1和a=2的值
也可以用把索引所在block dump出來檢視索引情況(索引所在檔案位置可由dba_extents的file_id,block_id確定)
trace檔案主要部分如下:
-----------------------------------------------------
row#0[8024] flag: -----, lock: 2
col 0; len 2; (2): c1 02 --這是列值
col 1; len 6; (6): 01 80 79 b2 00 00 --這是rowid
row#1[8012] flag: -----, lock: 2
col 0; len 2; (2): c1 03
col 1; len 6; (6): 01 80 79 b2 00 01
-----------------------------------------------------
我們可以透過如下方法驗證:
SQL> select to_number('c1','xxx'),to_number('02','xxxx'),to_number('c1','xxx'),to_number('03','xxxx') from dual;
TO_NUMBER('C1','XXX') TO_NUMBER('02','XXXX') TO_NUMBER('C1','XXX') TO_NUMBER('03','XXXX')
--------------------- ---------------------- --------------------- ----------------------
193 2 193 3
SQL> select dump(1),dump(2) from dual;
DUMP(1) DUMP(2)
------------------ ------------------
Typ=2 Len=2: 193,2 Typ=2 Len=2: 193,3
可以看出,只有a=1和a=2的記錄被索引了。
2、為什麼hints不起作用
> select * from t;
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=2 Bytes=18)
1 0 TABLE ACCESS (FULL) OF 'T' (Cost=2 Card=2 Bytes=18)
我們在某些時候需要使用hints來調整sql的執行計劃,比如我們可以使用/*+ index(table_name index_name) */來強制使用索引:
> select /*+ index(t idx_t) */ * from t;
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=82 Bytes=2132
)
1 0 TABLE ACCESS (FULL) OF 'T' (Cost=2 Card=82 Bytes=2132)
> select /*+ index(t idx_t) */ * from t where b=2;
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=1 Bytes=26)
1 0 TABLE ACCESS (FULL) OF 'T' (Cost=2 Card=1 Bytes=26)
這個執行計劃與我們預期不一樣,它並沒有按照hints使用索引來存取資料。這是為什麼呢?
上面的第一個sql的目的是返回t表的所有資料,第二個是要返回滿足條件b=2的所有資料。我們知道,B*tree索引是不會null值的,在以上的查詢中如果用索引的話,無論是直接從索引返回資料,還是根據索引掃描進而透過rowid獲取資料,都無法保證返回資料的正確性。oracle意識到這一點,忽略了強制使用索引的hints,透過全表掃描來返回資料。
3、用hints強制使用索引的前提
上文我們知道,是因為a列存在null值導致不能使用索引,那麼我們把a is null的記錄刪除了,是否可以使用hints呢?> delete from t where a is null;
已刪除 1 行。
> commit;
提交完成。
> select /*+ index(t idx_t) */ * from t;
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=82 Bytes=2132
)
1 0 TABLE ACCESS (FULL) OF 'T' (Cost=2 Card=82 Bytes=2132)
從執行計劃看出,即使表t的a列已經不存在null的情況,但仍然使用了全表掃描。這是因為oracle並不知道a列是否會存在null的情況(oracle不可能做到每次執行都遍歷一次表的資料檢視資料的分佈情況),為了保證返回資料的正確行,oracle選擇了全表掃描。
我們接著看:
> select /*+ index(t idx_t) */ * from t where a is not null;
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=2 Bytes=18)
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'T' (Cost=2 Card=2 Bytes=
18)
2 1 INDEX (FULL SCAN) OF 'IDX_T' (NON-UNIQUE) (Cost=1 Card=2
)
現在的執行計劃已經如我們所願,走索引了。這是因為我們限定了a不能為null,這樣就可以保證使用索引就可以返回所有需要的資料。
接著看:> select /*+ index(t idx_t) */ * from t where a=1;
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=1 Bytes=9)
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'T' (Cost=2 Card=1 Bytes=
9)
2 1 INDEX (RANGE SCAN) OF 'IDX_T' (NON-UNIQUE) (Cost=1 Card=
1)
執行計劃也可以走索引了,因為設定了a=1,已經限定了a肯定是一個非空的值了,可以從索引返回資料了。
再考察兩個例子:> select /*+ index(t idx_t) */ * from t where a<>1;
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=1 Bytes=9)
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'T' (Cost=2 Card=1 Bytes=
9)
2 1 INDEX (FULL SCAN) OF 'IDX_T' (NON-UNIQUE) (Cost=1 Card=1
)
> select /*+ index(t idx_t) */ * from t where a=null;
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=1 Bytes=9)
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'T' (Cost=2 Card=1 Bytes=
9)
2 1 INDEX (RANGE SCAN) OF 'IDX_T' (NON-UNIQUE) (Cost=1 Card=
1)
在oracle中,null=null和null<>null都不成立,null不能與任何值作大小比較。
由於null的這個特殊性,在條件a<>1中,已經忽略了a is null的情況,相當於已經隱含了a is not null條件了;
由於判斷某一個列是否為null只能用is [not] null,在a=null中,這裡的null被看作是一個常數(雖然它永遠為false),所以這個條件除了返回資料不一樣以外,其它作用與a=1是一樣的,也可以透過索引來獲取所有符合條件的資料。
再看下一個例子:
> alter table t modify(a not null);
表已更改。
> select /*+ index(t idx_t) */ * from t;
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=2 Bytes=18)
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'T' (Cost=2 Card=2 Bytes=
18)
2 1 INDEX (FULL SCAN) OF 'IDX_T' (NON-UNIQUE) (Cost=1 Card=2
)
我們對錶t的a列加上一個not null約束,orace知道索引會包含了a列的所有值,用索引可以返回所有符合條件的資料,索引hints起作用了。
從上面的例子可以看出,B*TREE索引不會索引null是整個問題的關鍵。因此,如果想要使用索引掃描,甚至是強制用/*+ index(table_name index_name) */強制使用索引,必須滿足以下三個條件中的一個或者多個:
(1)在想要用到的索引對應的欄位上有not null約束就可以用到索引。如果索引列有not null約束,無論條件怎麼寫都可能用到索引(包括col is null)
(2)如果索引列沒有not null約束,在where條件中限定了用到的索引對應的列為not null可以用到索引。如col1 is not null
(3)如果索引列沒有not null約束,且在條件中沒有指明索引列為not null,則在where條件中包含了用到的索引對應的列與其他值的比較(除col1 is null外)可以用到索引。假如索引對應列為a,則包含以下條件的查詢都可能用到索引:
a=b/a<>b/a>b/a
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/231499/viewspace-63750/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- NULL與索引Null索引
- 唯一索引,可以在索引列插入多個null嗎索引Null
- 索引與null(二):組合索引索引Null
- NULL 值與索引Null索引
- NULL 值與索引(二)Null索引
- 資料列not null對索引影響一例Null索引
- 認識SQLServer索引以及單列索引和多列索引的不同SQLServer索引
- 如何讓table表的null列由不走索引變為可走索引Null索引
- MySQL單列索引和組合索引的區別MySql索引
- 索引裡的NULL值與排序小記索引Null排序
- NULL列時,如何使得IS NULL或者IS NOT NULL可以使用索引來提高查詢效率Null索引
- 表為多列為null的表之索引示例Null索引
- MySQL中IS NULL、IS NOT NULL、!=不能用索引?胡扯!MySqlNull索引
- PostgreSQLGIN單列聚集索引應用SQL索引
- 聯合索引和多個單列索引使用中的索引命中情況及索引建立原則索引
- MySQL單列索引和組合索引的區別介紹MySql索引
- 索引失效系列——說說is null索引Null
- MySQL通過通用列索引來提供一個JSON列索引MySql索引JSON
- Oracle與GreatSQL差異:更改唯一索引列OracleSQL索引
- 資料庫聚簇索引——not null條件對唯一鍵索引成為聚簇索引的影響資料庫索引Null
- 為什麼索引無法使用is null和is not null索引Null
- SQLT 最佳化SQL 用複合索引代替單列索引的案例SQL索引
- Oracle vs PostgreSQL,研發注意事項(12) - NULL與索引OracleSQLNull索引
- MySQL 生成列索引MySql索引
- ORACLE 索引和MYSQL INNODB 輔助索引對NULL的處理區別Oracle索引MySqlNull
- 複合索引與函式索引優化一例索引函式優化
- oracle dml與索引index(一)Oracle索引Index
- 淺談索引系列之本地索引與全域性索引索引
- Mysql索引結構與索引原理MySql索引
- Mysql多列索引建立與優化.mdMySql索引優化
- 【TUNE_ORACLE】Oracle索引設計思想(一)索引片和匹配列概述Oracle索引
- MongoDB之索引(唯一索引)MongoDB索引
- 謂詞條件是is null走索引嗎?Null索引
- 兩列複合索引中(交換索引中列的順序),選索引的原則是?因為索引名字ascii碼小?索引ASCII
- 包含列的索引:SQL Server索引進階 Level 5索引SQLServer
- 跳過索引某些列任然使用索引的特性索引
- MySQL 唯一索引和普通索引MySql索引
- 【Oracle】-【建立索引】-建立索引的操作原理與一些體會Oracle索引