Hi tom,
I have two cases with me. Iam not sure why it happens that way. So here it is...
------------------------------------------------------------------------------------
CASE 1
------------------------------------------------------------------------------------
I have created a table T
SQL> create table t
2 as
3 select level id, rpad('*',100,'*') name
4 from dual
5 connect by level <= 100
6 /
Table created.
SQL> create index t_idx on t(id)
2 /
Index created.
SQL> exec dbms_stats.gather_table_stats(user,'T',cascade=>true)
PL/SQL procedure successfully completed.
Now lets see the explain plan when i issue a order by on id
SQL> delete from plan_table
2 /
0 rows deleted.
SQL> explain plan for select * from t order by id
2 /
Explained.
SQL> select * from table(dbms_xplan.display)
2 /
PLAN_TABLE_OUTPUT
---------------------------------------------------------------------------------------
Plan hash value: 961378228
---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 100 | 10400 | 4 (25)| 00:00:01 |
| 1 | SORT ORDER BY | | 100 | 10400 | 4 (25)| 00:00:01 |
| 2 | TABLE ACCESS FULL| T | 100 | 10400 | 3 (0)| 00:00:01 |
---------------------------------------------------------------------------
9 rows selected.
you can see the index is not considered. this is because there could be null values in the column.
so now i add a not null constraint
SQL> alter table t modify id not null
2 /
Table altered.
SQL> exec dbms_stats.gather_table_stats(user,'T',cascade=>true)
PL/SQL procedure successfully completed.
SQL> delete from plan_table
2 /
3 rows deleted.
SQL> explain plan for select * from t order by id
2 /
Explained.
SQL> select * from table(dbms_xplan.display)
2 /
PLAN_TABLE_OUTPUT
---------------------------------------------------------------------------------------
Plan hash value: 3778778741
-------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 100 | 10400 | 3 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| T | 100 | 10400 | 3 (0)| 00:00:01 |
| 2 | INDEX FULL SCAN | T_IDX | 100 | | 1 (0)| 00:00:01 |
-------------------------------------------------------------------------------------
9 rows selected.
see now it goes for an index.
when i go for a order by desc then also index will be considered.
SQL> delete from plan_table
2 /
3 rows deleted.
SQL> explain plan for select * from t order by id desc
2 /
Explained.
SQL> select * from table(dbms_xplan.display)
2 /
PLAN_TABLE_OUTPUT
---------------------------------------------------------------------------------------
Plan hash value: 2245626382
-------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 100 | 10400 | 3 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| T | 100 | 10400 | 3 (0)| 00:00:01 |
| 2 | INDEX FULL SCAN DESCENDING| T_IDX | 100 | | 1 (0)| 00:00:01 |
-------------------------------------------------------------------------------------
9 rows selected.
by specifing nulls last i believe oracle thinks the column may have null value and so its not considereing the index.
but not sure why its not considering the not null constraint and going for the index. can you please explain this.
SQL> delete from plan_table
2 /
3 rows deleted.
SQL> explain plan for select * from t order by id desc nulls last
2 /
Explained.
SQL> select * from table(dbms_xplan.display)
2 /
PLAN_TABLE_OUTPUT
---------------------------------------------------------------------------------------
Plan hash value: 961378228
---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 100 | 10400 | 4 (25)| 00:00:01 |
| 1 | SORT ORDER BY | | 100 | 10400 | 4 (25)| 00:00:01 |
| 2 | TABLE ACCESS FULL| T | 100 | 10400 | 3 (0)| 00:00:01 |
---------------------------------------------------------------------------
9 rows selected.
------------------------------------------------------------------------------------
CASE 2
------------------------------------------------------------------------------------
i have a table t
SQL> create table t
2 as
3 select level id, level id1, rpad('*',100,'*') name
4 from dual
5 connect by level <= 10
6 /
Table created.
SQL> alter table t modify id1 not null
2 /
Table altered.
SQL> create index t_idx on t(id,id1)
2 /
Index created.
SQL> exec dbms_stats.gather_table_stats(user,'T',cascade=>true)
PL/SQL procedure successfully completed.
so now when i order by id, id1 it consideres the index.
SQL> explain plan for select * from t order by id,id1
2 /
Explained.
SQL> select * from table(dbms_xplan.display)
2 /
PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------------------
Plan hash value: 3778778741
-------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------
PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 10 | 1070 | 2 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| T | 10 | 1070 | 2 (0)| 00:00:01 |
| 2 | INDEX FULL SCAN | T_IDX | 10 | | 1 (0)| 00:00:01 |
-------------------------------------------------------------------------------------
PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------------------
9 rows selected.
SQL> delete from plan_table
2 /
3 rows deleted.
for desc also index is considered.
SQL> explain plan for select * from t order by id desc, id1 desc
2 /
Explained.
SQL> select * from table(dbms_xplan.display)
2 /
PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------------------
Plan hash value: 2245626382
-------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------
PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 10 | 1070 | 2 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| T | 10 | 1070 | 2 (0)| 00:00:01 |
| 2 | INDEX FULL SCAN DESCENDING| T_IDX | 10 | | 1 (0)| 00:00:01 |
-------------------------------------------------------------------------------------
PLAN_TABLE_OUTPUT
-------------------------------------------------------------------------------------
9 rows selected.
SQL> delete from plan_table
2 /
3 rows deleted.
But again when i use nulls last index is not considered not sure why. please explain.
SQL> explain plan for select * from t order by id desc nulls last, id1 desc
2 /
Explained.
SQL> select * from table(dbms_xplan.display)
2 /
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 961378228
---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 10 | 1070 | 4 (25)| 00:00:01 |
| 1 | SORT ORDER BY | | 10 | 1070 | 4 (25)| 00:00:01 |
| 2 | TABLE ACCESS FULL| T | 10 | 1070 | 3 (0)| 00:00:01 |
---------------------------------------------------------------------------
9 rows selected.
Thanks,
Karthick.
before the column was made NOT NULL - it cannot use the index on t(id) to retrieve the rows from the table since it is a fact that if a row had id as NULL - that row would not have been in that index on t(id) - the answer would have been wrong.
Then, when you made id NOT NULL - we know that every row in the table is in the index and since the result is so teeny tiny (meaning "not really real world") it said "ok, lets use the index - it doesn't really matter if we use index or sort the data - it is so small anyway"
It is not really useful to test with this - if you were to create a real sized data set, you would find that it would never use your index. 10 rows is just too small for any sort of testing with indexes - at that size, it really doesn't matter HOW we access the data.
It could use the index, if it felt like it - your data is just way to small to be relevant and real world....
ops$tkyte%ORA10GR2> /*
ops$tkyte%ORA10GR2> drop table t;
ops$tkyte%ORA10GR2> create table t
ops$tkyte%ORA10GR2> as
ops$tkyte%ORA10GR2> select object_id id, a.*
ops$tkyte%ORA10GR2> from all_objects a
ops$tkyte%ORA10GR2> order by object_name;
ops$tkyte%ORA10GR2> create index t_idx on t(id);
ops$tkyte%ORA10GR2> exec dbms_stats.gather_table_stats(user,'T');
ops$tkyte%ORA10GR2> alter table t modify id NOT NULL;
ops$tkyte%ORA10GR2> */
ops$tkyte%ORA10GR2>
ops$tkyte%ORA10GR2> set autotrace traceonly explain
ops$tkyte%ORA10GR2> select * from t order by id;
Execution Plan
----------------------------------------------------------
Plan hash value: 961378228
-----------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
-----------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 50244 | 4808K| | 1377 (2)| 00:00:07 |
| 1 | SORT ORDER BY | | 50244 | 4808K| 12M| 1377 (2)| 00:00:07 |
| 2 | TABLE ACCESS FULL| T | 50244 | 4808K| | 239 (4)| 00:00:02 |
-----------------------------------------------------------------------------------
ops$tkyte%ORA10GR2> select /*+ index( t t_idx ) */ * from t order by id;
Execution Plan
----------------------------------------------------------
Plan hash value: 3778778741
-------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 50244 | 4808K| 37117 (1)| 00:02:57 |
| 1 | TABLE ACCESS BY INDEX ROWID| T | 50244 | 4808K| 37117 (1)| 00:02:57 |
| 2 | INDEX FULL SCAN | T_IDX | 50244 | | 116 (4)| 00:00:01 |
-------------------------------------------------------------------------------------
ops$tkyte%ORA10GR2> select /*+ index( t t_idx ) */ * from t order by id desc nulls last;
Execution Plan
----------------------------------------------------------
Plan hash value: 2364056610
----------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 50244 | 4808K| | 38256 (1)| 00:03:02 |
| 1 | SORT ORDER BY | | 50244 | 4808K| 12M| 38256 (1)| 00:03:02 |
| 2 | TABLE ACCESS BY INDEX ROWID| T | 50244 | 4808K| | 37117 (1)| 00:02:57 |
| 3 | INDEX FULL SCAN | T_IDX | 50244 | | | 116 (4)| 00:00:01 |
----------------------------------------------------------------------------------------------
ops$tkyte%ORA10GR2> set autotrace off
Now in the last case, it used the index but note the sort order by!!!
When you said "desc nulls last", we know that if we hit any nulls - they would be first in the index as we read it desc - not last, so we'd have to sort them. The optimizer is not using the NOT NULL constraint in this case, it is looking at your request to order by desc nulls last - and that is overriding the fact that the column is not null.
so - in short, if you don't have nulls, do not use nulls last/first.