patternModerate
Why isn't oracle using an index for distinct query ?
Viewed 0 times
distinctwhyisnqueryusingforindexoracle
Problem
This is the scenario
```
SQL> exec dbms_stats.gather_table_stats(user,'TM', cascade=>true)
PL/SQL procedure successfully completed.
SQL> SELECT SEGMENT_NAME , SEGMENT_TYPE , BYTES / 1024 / 1024 MB , BLOCKS FROM DBA_SEGMENTS WHERE SEGMENT_NAME IN ('TM', 'TM_LD_IX');
SEGMENT_NAME SEGMENT_TYPE MB BLOCKS
------------------------------------------ ---------- ----------
TM TABLE 296 37888
TM_LD_IX INDEX 46 5888
SQL> select index_name , column_name from user_ind_columns where index_name = 'TM_LD_IX';
INDEX_NAME COLUMN_NAME
------------ ------------------------------
TM_LD_IX LD
SQL> explain plan for select distinct LD from TM;
Explained.
SQL> @ex
PLAN_TABLE_OUTPUT
---------------------------------------------------------------------------------------------------------------------------------
Plan hash value: 4241255022
--------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 693 | 4158 | 7920 (8)| 00:01:36 |
| 1 | HASH UNIQUE | | 693 | 4158 | 7920 (8)| 00:01:36 |
| 2 | TABLE ACCESS FULL| TM | 2549K| 14M| 7486 (3)| 00:01:30 |
--------------------------------------------------------------------------------------
9 rows selected.
SQL> explain plan for select /+ index(x , TM_LD_IX) / distinct LD from TM x;
Explained.
SQL> @ex
PLAN_TABLE_OUTPUT
---------------------------------------------------------------------------------------------------------------------------------
Plan hash value: 4241255022
--------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Byt
```
SQL> exec dbms_stats.gather_table_stats(user,'TM', cascade=>true)
PL/SQL procedure successfully completed.
SQL> SELECT SEGMENT_NAME , SEGMENT_TYPE , BYTES / 1024 / 1024 MB , BLOCKS FROM DBA_SEGMENTS WHERE SEGMENT_NAME IN ('TM', 'TM_LD_IX');
SEGMENT_NAME SEGMENT_TYPE MB BLOCKS
------------------------------------------ ---------- ----------
TM TABLE 296 37888
TM_LD_IX INDEX 46 5888
SQL> select index_name , column_name from user_ind_columns where index_name = 'TM_LD_IX';
INDEX_NAME COLUMN_NAME
------------ ------------------------------
TM_LD_IX LD
SQL> explain plan for select distinct LD from TM;
Explained.
SQL> @ex
PLAN_TABLE_OUTPUT
---------------------------------------------------------------------------------------------------------------------------------
Plan hash value: 4241255022
--------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 693 | 4158 | 7920 (8)| 00:01:36 |
| 1 | HASH UNIQUE | | 693 | 4158 | 7920 (8)| 00:01:36 |
| 2 | TABLE ACCESS FULL| TM | 2549K| 14M| 7486 (3)| 00:01:30 |
--------------------------------------------------------------------------------------
9 rows selected.
SQL> explain plan for select /+ index(x , TM_LD_IX) / distinct LD from TM x;
Explained.
SQL> @ex
PLAN_TABLE_OUTPUT
---------------------------------------------------------------------------------------------------------------------------------
Plan hash value: 4241255022
--------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Byt
Solution
The reason for this behaviour is that rows where LD is NULL cannot be found in the index. Therefore Oracle has to scan the full table. If the table is created with LD as a NOT NULL column then the optimizer uses this information and does an INDEX FAST FULL SCAN. If you add a "CHECK(LD is not null)" constraint to the table that has not NOT NULL defined for the column LD then the optimizer does not use the information provided by the constraint and makes a full table scan again, even if you gave him a hint. Jonathan Lewis wrote about this behaviour.
The following scripts demonstrate this behaviour for Oracle 11.2.0.3.0
create_table.sql inserts data into the table and creates index and statistics
set autotrace off
drop table objects
/
create table objects(
object_id number,
owner varchar2(30),
object_name varchar2(128),
object_type varchar2(19)
)
/
insert into objects(
object_id,
owner,
object_name,
object_type
)
select
object_id,
owner,
object_name,
object_type
from dba_objects
/
create index idx_object_id on objects(object_id);
exec dbms_stats.gather_table_stats(user,'objects', cascade=>true)
Now run the following script:
spool output
set feedback off
set linesize 300
set trimout on
set trimspool on
@create_table
set autotrace traceonly explain
prompt
prompt 1. plan for query with no constraints:
select distinct object_id
from objects;
rem ---------------------------------------------------
@create_table
alter table objects add constraint nn_object_id check(object_id is not null) validate;
set autotrace traceonly explain
prompt
prompt 2. plan for query with check constraint
select distinct object_id
from objects;
rem ---------------------------------------------------
@create_table
alter table objects modify object_id not null;
set autotrace traceonly explain
prompt
prompt 3.plan for query with NOT NULL column
select distinct object_id
from objects;
rem ---------------------------------------------------
@create_table
create bitmap index bidx_object_type on objects(object_type)
/
set autotrace traceonly explain
prompt
prompt 4.plan for query with bitmap index
select distinct object_type
from objects;
rem ---------------------------------------------------
spool off
This gives the following output
Execution Plan
----------------------------------------------------------
Plan hash value: 4077265613
------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 59063 | 288K| 139 (3)| 00:00:02 |
| 1 | HASH UNIQUE | | 59063 | 288K| 139 (3)| 00:00:02 |
| 2 | TABLE ACCESS FULL| OBJECTS | 59063 | 288K| 136 (0)| 00:00:02 |
------------------------------------------------------------------------------
Execution Plan
----------------------------------------------------------
Plan hash value: 4077265613
------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 59063 | 288K| 139 (3)| 00:00:02 |
| 1 | HASH UNIQUE | | 59063 | 288K| 139 (3)| 00:00:02 |
| 2 | TABLE ACCESS FULL| OBJECTS | 59063 | 288K| 136 (0)| 00:00:02 |
------------------------------------------------------------------------------
3.plan for query with NOT NULL column
Execution Plan
----------------------------------------------------------
Plan hash value: 4172758181
---------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 59063 | 288K| 40 (8)| 00:00:01 |
| 1 | HASH UNIQUE | | 59063 | 288K| 40 (8)| 00:00:01 |
| 2 | INDEX FAST FULL SCAN| IDX_OBJECT_ID | 59063 | 288K| 37 (0)| 00:00:01 |
---------------------------------------------------------------------------------------
4.plan for query with bitmap index
Execution Plan
----------------------------------------------------------
Plan hash value: 2970019208
-------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 43 | 387 | 6 (34)| 00:00:01 |
| 1 | HASH UNIQUE
The following scripts demonstrate this behaviour for Oracle 11.2.0.3.0
create_table.sql inserts data into the table and creates index and statistics
set autotrace off
drop table objects
/
create table objects(
object_id number,
owner varchar2(30),
object_name varchar2(128),
object_type varchar2(19)
)
/
insert into objects(
object_id,
owner,
object_name,
object_type
)
select
object_id,
owner,
object_name,
object_type
from dba_objects
/
create index idx_object_id on objects(object_id);
exec dbms_stats.gather_table_stats(user,'objects', cascade=>true)
Now run the following script:
spool output
set feedback off
set linesize 300
set trimout on
set trimspool on
@create_table
set autotrace traceonly explain
prompt
prompt 1. plan for query with no constraints:
select distinct object_id
from objects;
rem ---------------------------------------------------
@create_table
alter table objects add constraint nn_object_id check(object_id is not null) validate;
set autotrace traceonly explain
prompt
prompt 2. plan for query with check constraint
select distinct object_id
from objects;
rem ---------------------------------------------------
@create_table
alter table objects modify object_id not null;
set autotrace traceonly explain
prompt
prompt 3.plan for query with NOT NULL column
select distinct object_id
from objects;
rem ---------------------------------------------------
@create_table
create bitmap index bidx_object_type on objects(object_type)
/
set autotrace traceonly explain
prompt
prompt 4.plan for query with bitmap index
select distinct object_type
from objects;
rem ---------------------------------------------------
spool off
This gives the following output
- plan for query with no constraints:
Execution Plan
----------------------------------------------------------
Plan hash value: 4077265613
------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 59063 | 288K| 139 (3)| 00:00:02 |
| 1 | HASH UNIQUE | | 59063 | 288K| 139 (3)| 00:00:02 |
| 2 | TABLE ACCESS FULL| OBJECTS | 59063 | 288K| 136 (0)| 00:00:02 |
------------------------------------------------------------------------------
- plan for query with check constraint
Execution Plan
----------------------------------------------------------
Plan hash value: 4077265613
------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 59063 | 288K| 139 (3)| 00:00:02 |
| 1 | HASH UNIQUE | | 59063 | 288K| 139 (3)| 00:00:02 |
| 2 | TABLE ACCESS FULL| OBJECTS | 59063 | 288K| 136 (0)| 00:00:02 |
------------------------------------------------------------------------------
3.plan for query with NOT NULL column
Execution Plan
----------------------------------------------------------
Plan hash value: 4172758181
---------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 59063 | 288K| 40 (8)| 00:00:01 |
| 1 | HASH UNIQUE | | 59063 | 288K| 40 (8)| 00:00:01 |
| 2 | INDEX FAST FULL SCAN| IDX_OBJECT_ID | 59063 | 288K| 37 (0)| 00:00:01 |
---------------------------------------------------------------------------------------
4.plan for query with bitmap index
Execution Plan
----------------------------------------------------------
Plan hash value: 2970019208
-------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 43 | 387 | 6 (34)| 00:00:01 |
| 1 | HASH UNIQUE
Context
StackExchange Database Administrators Q#48109, answer score: 10
Revisions (0)
No revisions yet.