为什么在查询中使用IS NULL 或IS NOT NULL同样会限制索引的使用
简答的说因为索引键值不会存储空值
Oracle的CBO并不会因为SQL语句中指定了IS NOT NULL或IS NULL操作就不再使用索引。CBO选择索引的条件只有正确性和代价,只要满足这两个条件就可以了。
测试案例
以dba_objects表的数据建一个测试表为T
create table t as select * from dba_objects;
将data_object_id=2的object_name列更改为null
update t set object_name =null where data_object_id=2 and (object_id <20 or object_id>20)
创建object_name列的索引
create index index_objecton on t(object_name)
B树索引不存储键值全为空的记录。所以对于单列索引而言,确实IS NULL操作是无法使用索引的
—————————————————————————————————-
SQL> select count(*) from t where object_name is null; ———走的是全表扫描
Execution Plan
———————————————————-
Plan hash value: 2966233522
—————————————————————————
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
—————————————————————————
| 0 | SELECT STATEMENT | | 1 | 25 | 283 (1)| 00:00:04 |
| 1 | SORT AGGREGATE | | 1 | 25 | | |
|* 2 | TABLE ACCESS FULL| T | 17 | 425 | 283 (1)| 00:00:04 |
—————————————————————————
Predicate Information (identified by operation id):
—————————————————
2 – filter(“OBJECT_NAME” IS NULL)
Statistics
———————————————————-
1 recursive calls
0 db block gets
1038 consistent gets
1035 physical reads
0 redo size
422 bytes sent via SQL*Net to client
419 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
SQL> select count(*) from t where object_name is not null; —————-走的是索引,,
Execution Plan
———————————————————-
Plan hash value: 162321089
—————————————————————————————-
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
—————————————————————————————-
| 0 | SELECT STATEMENT | | 1 | 25 | 100 (0)| 00:00:02 |
| 1 | SORT AGGREGATE | | 1 | 25 | | |
|* 2 | INDEX FAST FULL SCAN| INDEX_OBJECTON | 72749 | 1776K| 100 (0)| 00:00:02 |
—————————————————————————————-
Predicate Information (identified by operation id):
—————————————————
2 – filter(“OBJECT_NAME” IS NOT NULL)
Statistics
———————————————————-
0 recursive calls
0 db block gets
371 consistent gets
0 physical reads
0 redo size
424 bytes sent via SQL*Net to client
419 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
—————————————————————————————————————————————————
复合索引是可能存储一部分NULL值的,所以,IS NULL操作也并非不可能使用索引。
测试环境
ALTER TABLE T MODIFY OWNER NOT NULL; ———–修改字段类型
UPDATE T SET OBJECT_ID = NULL WHERE ROWNUM = 1;
CREATE INDEX IND_T_OBJECT_OWNER ON T (OBJECT_ID, OWNER); ——-创建组合索引
EXEC DBMS_STATS.GATHER_TABLE_STATS(USER, ‘T’, METHOD_OPT => ‘FOR ALL INDEXED COLUMNS SIZE 200’)
SQL> select count(*) from t where object_id is null; ————走索引
Execution Plan
———————————————————-
Plan hash value: 4092988493
—————————————————————————————-
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Ti
me |
—————————————————————————————-
| 0 | SELECT STATEMENT | | 1 | 5 | 2 (0)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | 5 | | |
|* 2 | INDEX RANGE SCAN| IND_T_OBJECT_OWNER | 1 | 5 | 2 (0)| 00:00:01 |
—————————————————————————————-
Predicate Information (identified by operation id):
—————————————————
2 – access(“OBJECT_ID” IS NULL)
Statistics
———————————————————-
1 recursive calls
0 db block gets
2 consistent gets
0 physical reads
0 redo size
422 bytes sent via SQL*Net to client
419 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed