Home » RDBMS Server » Performance Tuning » Index usage in order by clause on nullable column (Oracle 10gR2 on RHEL)
Index usage in order by clause on nullable column [message #490944] Fri, 28 January 2011 04:19 Go to next message
OraKaran
Messages: 183
Registered: March 2008
Location: United Kingdom
Senior Member
Hi

I came across situation where a Nullable column is not using index for 'order by' clause.
I added Not Null condition in the 'where' condition but it wasn't useful.
I don't wanted to make composite index with not nullable column or with constant or modify column to 'Not Null'

So I carried out test cases and during which I found that in one case the sql statement does 'fast full scan' for data access but does not use index for 'order by' sorting

here are the steps

Initially I kept the column Nullable

SQL> create sequence s5;
Sequence created.

SQL> create table t5 as select s5.nextval id,a.* from  dba_objects a where rownum<1001;
Table created.

SQL> set pages 100
SQL> select column_name,nullable from user_tab_columns where table_name='T5';


SQL> create index i5 on t5(id);
Index created.

SQL> exec dbms_stats.gather_table_stats(user,'T5',cascade=>true);
PL/SQL procedure successfully completed.
exit

SQL> alter session set events '10046 trace name context forever, level 12';


select *
from
 t5 where id is not null order by id


call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch       68      0.00       0.00          0         16          0        1000
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total       70      0.01       0.00          0         16          0        1000

Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 5

Rows     Row Source Operation
-------  ---------------------------------------------------
   1000  SORT ORDER BY (cr=16 pr=0 pw=0 time=4771 us)
   1000   TABLE ACCESS FULL T5 (cr=16 pr=0 pw=0 time=1157 us)


Elapsed times include waiting on following events:
  Event waited on                             Times   Max. Wait  Total Waited
  ----------------------------------------   Waited  ----------  ------------
  SQL*Net message to client                      68        0.00          0.00
  SQL*Net message from client                    68       49.49         49.72
********************************************************************************

select /*+ index(t i5) */ *
from
 t5 t where id is not null order by id


call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch       68      0.00       0.00          0        150          0        1000
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total       70      0.00       0.00          0        150          0        1000

Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 5

Rows     Row Source Operation
-------  ---------------------------------------------------
   1000  TABLE ACCESS BY INDEX ROWID T5 (cr=150 pr=0 pw=0 time=5167 us)
   1000   INDEX FULL SCAN I5 (cr=71 pr=0 pw=0 time=3141 us)(object id 4673065)


Elapsed times include waiting on following events:
  Event waited on                             Times   Max. Wait  Total Waited
  ----------------------------------------   Waited  ----------  ------------
  SQL*Net message to client                      69        0.00          0.00
  SQL*Net message from client                    69       22.89         28.04


Now I modified the 'id' column to Not Null

SQL> alter table t5 modify id not null;

SQL> set pages 100
SQL> select column_name,nullable from user_tab_columns where table_name='T5';

COLUMN_NAME                    N
------------------------------ -
ID                             N
OWNER                          Y
OBJECT_NAME                    Y
SUBOBJECT_NAME                 Y
OBJECT_ID                      Y
DATA_OBJECT_ID                 Y
OBJECT_TYPE                    Y
CREATED                        Y
LAST_DDL_TIME                  Y
TIMESTAMP                      Y
STATUS                         Y
TEMPORARY                      Y
GENERATED                      Y
SECONDARY                      Y

14 rows selected.


select *
from
 t5 order by id


call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.01          0         29          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch       68      0.00       0.00          0         16          0        1000
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total       70      0.01       0.01          0         45          0        1000

Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 5

Rows     Row Source Operation
-------  ---------------------------------------------------
   1000  SORT ORDER BY (cr=16 pr=0 pw=0 time=2398 us)
   1000   TABLE ACCESS FULL T5 (cr=16 pr=0 pw=0 time=1152 us)


Elapsed times include waiting on following events:
  Event waited on                             Times   Max. Wait  Total Waited
  ----------------------------------------   Waited  ----------  ------------
  SQL*Net message to client                      68        0.00          0.00
  SQL*Net message from client                    68       37.74         37.91
********************************************************************************

select /*+ index(t i5) */ *
from
 t5 t order by id


call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch       68      0.00       0.00          0        150          0        1000
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total       70      0.00       0.00          0        150          0        1000

Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 5

Rows     Row Source Operation
-------  ---------------------------------------------------
   1000  TABLE ACCESS BY INDEX ROWID T5 (cr=150 pr=0 pw=0 time=4166 us)
   1000   INDEX FULL SCAN I5 (cr=71 pr=0 pw=0 time=3142 us)(object id 4673065)


Elapsed times include waiting on following events:
  Event waited on                             Times   Max. Wait  Total Waited
  ----------------------------------------   Waited  ----------  ------------
  SQL*Net message to client                      68        0.00          0.00
  SQL*Net message from client                    68        8.28          8.45

select id
from
 t5 order by id


call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch       68      0.00       0.00          0          6          0        1000
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total       70      0.00       0.00          0          6          0        1000

Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 5

Rows     Row Source Operation
-------  ---------------------------------------------------
   1000  SORT ORDER BY (cr=6 pr=0 pw=0 time=1342 us)
   1000   INDEX FAST FULL SCAN I5 (cr=6 pr=0 pw=0 time=1093 us)(object id 4673065)


Elapsed times include waiting on following events:
  Event waited on                             Times   Max. Wait  Total Waited
  ----------------------------------------   Waited  ----------  ------------
  SQL*Net message to client                      68        0.00          0.00
  SQL*Net message from client                    68        1.88          1.89


Questions are
1) Why adding 'where id is not null wasn't enough for the index to get used in 'order by'?
2) While we got 'fast full scan' why index wasn't used for 'order by' clause?
3) Do we need the indexed column in where clause for being used in 'order by clause' too?
4) Do we need 'order by' clause if we are selecting only the indexed column with sequence generated values?

Thanks and Regards,
OraKaran
Re: Index usage in order by clause on nullable column [message #490950 is a reply to message #490944] Fri, 28 January 2011 04:29 Go to previous messageGo to next message
Michel Cadot
Messages: 68645
Registered: March 2007
Location: Nanterre, France, http://...
Senior Member
Account Moderator
Quote:
I came across situation where a Nullable column is not using index for 'order by' clause.

There is no reason Oracle should use an index for an "order by" clause.
It may use it in specific case; for instance, when the last operation in an "index range scan" and the columns of the index are the one of the "order by" but this just means that with or without "order by" the index will be used and it takes profit, in this case, of the index to skip the "order by" operation.
In any case, Oracle will trigger an index operation just to satisfy an "order by" clause.

Regards
Michel
Re: Index usage in order by clause on nullable column [message #490965 is a reply to message #490950] Fri, 28 January 2011 05:16 Go to previous messageGo to next message
OraKaran
Messages: 183
Registered: March 2008
Location: United Kingdom
Senior Member
Hello Michel

I understood the intitial part of the reply

I am re-reading the following line
Quote:

In any case, Oracle will trigger an index operation just to satisfy an "order by" clause


The Metalink note 418708.1 has covered some cases but they have forced optimizer mode for index usage in the cases and mainly the topic focuses on nullable column

Could you please suggest on my 4th question?
Quote:

4) Do we need 'order by' clause if we are selecting only the indexed column with sequence generated values?


Regards,
OraKaran

Re: Index usage in order by clause on nullable column [message #490984 is a reply to message #490965] Fri, 28 January 2011 06:29 Go to previous messageGo to next message
Michel Cadot
Messages: 68645
Registered: March 2007
Location: Nanterre, France, http://...
Senior Member
Account Moderator
Quote:
The Metalink note 418708.1 has covered some cases but they have forced optimizer mode for index usage in the cases and mainly the topic focuses on nullable column

OK it seems it does it for this specific case. Maybe it will not do it for the same query with a different number of rows. It is just a matter of comparing cost of "full table scan + sort" and "full index scan + table access for each row". It surely greatly depends on clustering factor and so on the life of the table data.

Quote:
4) Do we need 'order by' clause if we are selecting only the indexed column with sequence generated values?

Yes, there is NOT exception, each time you want a specific order you MUST give an ORDER BY clause. ALWAYS.

Regards
Michel
Re: Index usage in order by clause on nullable column [message #491032 is a reply to message #490944] Fri, 28 January 2011 09:05 Go to previous messageGo to next message
hkchital
Messages: 128
Registered: September 2008
Location: Singapore
Senior Member
When you are issuing a "select * ...", Oracle knows that it will have to go to the table to retrieve other column(s), the query cannot be satisfied by the index alone. Next it has to evaluate whether it should use the Index to identify the candidate rows before going to the table OR whether it should just go to the table directly (i.e. do a FullTableScan -- which appears as TABLE FULL SCAN).

If you look at the tkprof output, the 1000 rows FullTableScan was satified by reading only 16 blocks. Even assuming 8block reads, it is only 2 read calls to the OS. On the other hand, when you added the Index Hint, you forced Oracle to read the Index and then go to the table for each row. This total effort was 150 blocks to be read. Therefore, the Index was a poor choice to execute the "select *..." query and the optimizer was correct to not use it when left to make a decision on it's own.

On to your questions :
1) Why adding 'where id is not null wasn't enough for the index to get used in 'order by'?
The FullTableScan was the faster and better way to retrieve the data you required, even if it had to sort the 1000 rows. Remember that the order by was the last operation -- it had to first retrieve the 1000 rows from the table block.

2) While we got 'fast full scan' why index wasn't used for 'order by' clause?
The query where Oracle did an IndexFastFullScan was only for the 'id' column. Oracle did not have to go to the table blocks. This query could be satisfied by the Index alone. The FastFullScan of the Index is like the MultiBlock Read (FullTableScan) of a table -- with as few I/O calls as necessary. It had to read 6 blocks -- which wa satisfied with one single I/O call. It is easier for Oracle to sort the rows after the read call.
Had Oracle chosen to do an Index Full Scan (which is how it could have retrieved the 'id' values in an ordered manner), it would have had to read the Index Blocks in SingleBlock Read Calls. That would have been slower.

3) Do we need the indexed column in where clause for being used in 'order by clause' too?
No it is not necessary. If Oracle finds that it can use an Index Full Scan, it retrieves the values ordered. However, a query may have more than one ORDER BY column -- and not all of them may be indexed or in the same index. It is not necesssary that the ORDER BY column be indexed. (e.g. think of the GROUP BY ... ORDER BY where ordering is done on values that are grouped after they are fetched from the table).

4) Do we need 'order by' clause if we are selecting only the indexed column with sequence generated values?
An ORDER BY decision is driven by how you want to present the data. Don't link the ORDER BY with the Index (although Oracle can use an Index to retrieve the ordered rows (see my previous answer)).


Hemant K Chitale

[Updated on: Fri, 28 January 2011 09:14] by Moderator

Report message to a moderator

Re: Index usage in order by clause on nullable column [message #491070 is a reply to message #491032] Fri, 28 January 2011 19:12 Go to previous message
rleishman
Messages: 3728
Registered: October 2005
Location: Melbourne, Australia
Senior Member
Try replacing
WHERE  id is not null

with
WHERE  id >= -9999999999999999

or
WHERE  id >= chr(0)

for VARCHAR2 columns. You may also need to add an index hint, but providing you don't have any other WHERE clause, you should be able to force the index range scan (even if it is slower than a FULL TABLE SCAN).

Ross Leishman
Previous Topic: Using a stats table instead of regular stats to get a plan
Next Topic: Disable ADR
Goto Forum:
  


Current Time: Thu Apr 25 09:58:20 CDT 2024