I have a non-clustered index on this column (TOM
) in this table (SP_ORD_1PC_PASS
).
This query returns instantly:
SELECT MAX(TOM) FROM SP_ORD_1PC_PASS
This query also returns instantly:
SELECT MIN(TOM) FROM SP_ORD_1PC_PASS
However, this query takes 3 minutes to return:
SELECT MIN(TOM), MAX(TOM) FROM SP_ORD_1PC_PASS
Anything I try to do with TOM
column besides a single SELECT MIN
or SELECT MAX
results in very long query times.
I've never seen this type of behavior on an indexed column.
Other notes:
I rebuilt the index - no difference in results.
I've tried sorting same table by other columns that are indexed, and I have no issues.
If it helps, here are the index stats:
EDIT
I did alot of testing with this.
This query returns in 3 minutes:
SELECT * FROM SP_ORD_1PC_PASS
ORDER BY TOM ASC
This query returns instantly:
SELECT * FROM SP_ORD_1PC_PASS
WHERE TOM > SYSDATE-1000000
ORDER BY TOM ASC
... even though the WHERE
clause doesn't exclude any data from the query result ...
I guess, problem solved. But... why???
For the first problem, the MIN/MAX index optimization only works for either MIN or MAX, but not both. But a simple rewrite of the query into two subqueries will enable the index access, as described in this answer here.
For the second problem, the condition TOM > SYSDATE-1000000
provides useful information to Oracle that enables index access. While you may know that the column TOM is always non-null, Oracle does not know unless you create a non-null constraint. Without that constraint, Oracle must assume that the column contains a NULL value. And since B-tree indexes do not (usually) contain NULLs, the index cannot in the query. But with that new condition, only non-NULL values could possibly be returned, so Oracle knows the index access is possible. (This is why constraints are an under-appreciated way to improve performance.)
-- drop table sp_ord_1pc_pass;
create table sp_ord_1pc_pass(a number, tom date);
insert into sp_ord_1pc_pass
select level, sysdate + level
from dual
connect by level <= 400000;
create index sp_ord_1pc_pass_idx on sp_ord_1pc_pass(tom);
begin
dbms_stats.gather_table_stats(user, 'SP_ORD_1PC_PASS');
end;
/
This explain plan sorts all of the data before any is returned to the client, which makes it look slow.
explain plan for
select *
from sp_ord_1pc_pass
order by tom asc;
select * from table(dbms_xplan.display(format => 'basic'));
Plan hash value: 3972713478
----------------------------------------------
| Id | Operation | Name |
----------------------------------------------
| 0 | SELECT STATEMENT | |
| 1 | SORT ORDER BY | |
| 2 | TABLE ACCESS FULL| SP_ORD_1PC_PASS |
----------------------------------------------
This explain plan reads an index in order, so it will start returning data almost immediately. However, the total time to return all the results is likely actually going to take longer than a full table scan and sort. This query most likely only feels faster in an IDE that only returns the top 50 rows.
explain plan for
select * from sp_ord_1pc_pass
where tom > sysdate-1000000
order by tom asc;
select * from table(dbms_xplan.display(format => 'basic'));
Plan hash value: 1314632260
-----------------------------------------------------------
| Id | Operation | Name |
-----------------------------------------------------------
| 0 | SELECT STATEMENT | |
| 1 | TABLE ACCESS BY INDEX ROWID| SP_ORD_1PC_PASS |
| 2 | INDEX RANGE SCAN | SP_ORD_1PC_PASS_IDX |
-----------------------------------------------------------