We had question in the OpenWorld panel about why queries on date columns are “always slow”. Well….they aren’t but here’s a common cause of that misconception.
Let’s create a table as a copy of DBA_OBJECTS, and index the CREATED column (which is a date).
SQL> create table t as
2 select *
3 from dba_objects;
Table created.
SQL>
SQL> create index t_ix on t ( created );
Index created.
The problems start when we do a query on the CREATED column and get “unexpected” results
SQL> select owner, object_name
2 from t
3 where created = date '2016-09-20';
no rows selected
We were expecting to get some rows here, but none were returned. And we quickly deduce that this is because of the CREATED column also containing the time component. So nothing was created at midnight on September 20.
So to remove the time component, the query is recast as:
SQL> set autotrace on
SQL> select owner, object_name
2 from t
3 where trunc(created) = date '2016-09-20';
OWNER OBJECT_NAME
------------ ------------------------------
MCDONAC T1
Execution Plan
----------------------------------------------------------
Plan hash value: 1601196873
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 951 | 38040 | 446 (1)| 00:00:01 |
|* 1 | TABLE ACCESS FULL| T | 951 | 38040 | 446 (1)| 00:00:01 |
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(TRUNC(INTERNAL_FUNCTION("CREATED"))=TO_DATE(' 2016-09-20
00:00:00', 'syyyy-mm-dd hh24:mi:ss'))
Statistics
----------------------------------------------------------
1 recursive calls
0 db block gets
1602 consistent gets
0 physical reads
0 redo size
620 bytes sent via SQL*Net to client
552 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
and ker-splat, the ability to use the index on the CREATED column disappears because we put the TRUNC expression around it. We used 1602 logical I/O’s to satisfy our query. But all it takes is a little tinkering of our query to get CREATED “unwrapped”
SQL> select owner, object_name
2 from t
3 where created >= date '2016-09-20'
4 and created < date '2016-09-20' + 1;
OWNER OBJECT_NAME
------------ ------------------------------
MCDONAC T1
Execution Plan
----------------------------------------------------------
Plan hash value: 3343387620
--------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 184 | 7360 | 6 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID BATCHED| T | 184 | 7360 | 6 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | T_IX | 184 | | 2 (0)| 00:00:01 |
--------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("CREATED">=TO_DATE(' 2016-09-20 00:00:00', 'syyyy-mm-dd hh24:mi:ss')
AND "CREATED"< processed rows 1 (disk) sorts 0 (memory) client from to roundtrips SQL*Net 2 via received bytes 552 sent 620 size redo reads physical gets consistent 4 block db calls recursive ---------------------------------------------------------- Statistics hh24:mi:ss?)) ?syyyy-mm-dd 00:00:00?, 2016-09-21>
The query is the same functionally, but we got to use the index to speed up our query. This is not by any means claiming that using the index is always the best option, but at least by having the CREATED column “untarnished” by expressions, then we are giving the optimizer more choices on the potential means to best run the query.
Got some thoughts? Leave a comment