Why is Oracle ignoring index with ORDER BY? Why is Oracle ignoring index with ORDER BY? sql sql

Why is Oracle ignoring index with ORDER BY?


more than likely your FIRST_NAME column is nullable.

SQL> create table customer (first_name varchar2(20), last_name varchar2(20));Table created.SQL> insert into customer select dbms_random.string('U', 20), dbms_random.string('U', 20) from dual connect by level <= 100000;100000 rows created.SQL> create index c on customer(first_name);Index created.SQL> explain plan for select * from (  2    select /*+ FIRST_ROWS(20) */ FIRST_NAME, ROW_NUMBER() over (order by FIRST_NAME) RN  3    from CUSTOMER C  4  )  5  where RN between 1 and 20  6  order by RN;Explained.SQL> @explain ""Plan hash value: 1474094583----------------------------------------------------------------------------------------------| Id  | Operation                 | Name     | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |----------------------------------------------------------------------------------------------|   0 | SELECT STATEMENT          |          |   117K|  2856K|       |  1592   (1)| 00:00:20 ||   1 |  SORT ORDER BY            |          |   117K|  2856K|  4152K|  1592   (1)| 00:00:20 ||*  2 |   VIEW                    |          |   117K|  2856K|       |   744   (2)| 00:00:09 ||*  3 |    WINDOW SORT PUSHED RANK|          |   117K|  1371K|  2304K|   744   (2)| 00:00:09 ||   4 |     TABLE ACCESS FULL     | CUSTOMER |   117K|  1371K|       |   205   (1)| 00:00:03 |----------------------------------------------------------------------------------------------Predicate Information (identified by operation id):---------------------------------------------------   2 - filter("RN">=1 AND "RN"<=20)   3 - filter(ROW_NUMBER() OVER ( ORDER BY "FIRST_NAME")<=20)Note-----   - dynamic sampling used for this statement (level=2)21 rows selected.SQL> alter table customer modify first_name not null;Table altered.SQL> explain plan for select * from (  2    select /*+ FIRST_ROWS(20) */ FIRST_NAME, ROW_NUMBER() over (order by FIRST_NAME) RN  3    from CUSTOMER C  4  )  5  where RN between 1 and 20  6  order by RN;Explained.SQL> @explain ""Plan hash value: 1725028138----------------------------------------------------------------------------------------| Id  | Operation               | Name | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |----------------------------------------------------------------------------------------|   0 | SELECT STATEMENT        |      |   117K|  2856K|       |   850   (1)| 00:00:11 ||   1 |  SORT ORDER BY          |      |   117K|  2856K|  4152K|   850   (1)| 00:00:11 ||*  2 |   VIEW                  |      |   117K|  2856K|       |     2   (0)| 00:00:01 ||*  3 |    WINDOW NOSORT STOPKEY|      |   117K|  1371K|       |     2   (0)| 00:00:01 ||   4 |     INDEX FULL SCAN     | C    |   117K|  1371K|       |     2   (0)| 00:00:01 |----------------------------------------------------------------------------------------Predicate Information (identified by operation id):---------------------------------------------------   2 - filter("RN">=1 AND "RN"<=20)   3 - filter(ROW_NUMBER() OVER ( ORDER BY "FIRST_NAME")<=20)Note-----   - dynamic sampling used for this statement (level=2)21 rows selected.SQL>

add a NOT NULL in there to resolve it.

SQL> explain plan for select * from (  2    select /*+ FIRST_ROWS(20) */ FIRST_NAME, ROW_NUMBER() over (order by FIRST_NAME) RN  3    from CUSTOMER C  4    where first_name is not null  5  )  6  where RN between 1 and 20  7  order by RN;Explained.SQL> @explain ""Plan hash value: 1725028138----------------------------------------------------------------------------------------| Id  | Operation               | Name | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |----------------------------------------------------------------------------------------|   0 | SELECT STATEMENT        |      |   117K|  2856K|       |   850   (1)| 00:00:11 ||   1 |  SORT ORDER BY          |      |   117K|  2856K|  4152K|   850   (1)| 00:00:11 ||*  2 |   VIEW                  |      |   117K|  2856K|       |     2   (0)| 00:00:01 ||*  3 |    WINDOW NOSORT STOPKEY|      |   117K|  1371K|       |     2   (0)| 00:00:01 ||*  4 |     INDEX FULL SCAN     | C    |   117K|  1371K|       |     2   (0)| 00:00:01 |----------------------------------------------------------------------------------------Predicate Information (identified by operation id):---------------------------------------------------   2 - filter("RN">=1 AND "RN"<=20)   3 - filter(ROW_NUMBER() OVER ( ORDER BY "FIRST_NAME")<=20)   4 - filter("FIRST_NAME" IS NOT NULL)Note-----   - dynamic sampling used for this statement (level=2)22 rows selected.SQL>


You're querying for more columns than first_name. The index on first_name just contains the first_name column and a reference to the table. So to retrieve the other columns, Oracle has to perform a lookup to the table itself for each row. Most databases try to avoid this if they can't guarantee a low record count.

A database is typically not smart enough to know the effects of a where clause on a row_number column. However, your hint /*+ FIRST_ROWS(20) */ might have done the trick.

Perhaps the table is really small, so that Oracle expects the table scan to be cheaper than lookups, even for just 20 rows.