Partition pruning is only possible in presence of partition key
columns in predicates, but this scenario recently appeared in an application that
breaks the rule.
demo@ORA11G> create table t
2 partition by range(end_date)
3 ( partition p2010 values less than
(to_date('01-Jan-2011','dd-mon-yyyy')) ,
4 partition p2011 values less than
(to_date('01-Jan-2012','dd-mon-yyyy')) ,
5 partition p2012 values less than
(to_date('01-Jan-2013','dd-mon-yyyy')) ,
6 partition p2013 values less than
(to_date('01-Jan-2014','dd-mon-yyyy')) ,
7 partition p2014 values less than
(to_date('01-Jan-2015','dd-mon-yyyy')) ,
8 partition p2015 values less than
(to_date('01-Jan-2016','dd-mon-yyyy')) ,
9 partition p2016 values less than
(to_date('01-Jan-2017','dd-mon-yyyy')) ,
10 partition p2017 values less than
(to_date('01-Jan-2018','dd-mon-yyyy')) )
11 as
12 select a.* , created-1 as begin_date,
13 created+1 as end_date
14 from all_objects a;
Table created.
demo@ORA11G> exec dbms_stats.gather_table_stats(user,'T');
PL/SQL procedure successfully completed.
demo@ORA11G> set autotrace traceonly explain
demo@ORA11G> select *
2 from t
3 where begin_date between
4 to_date('01-apr-2016','dd-mon-yyyy') and
5 to_date('15-apr-2016','dd-mon-yyyy') ;
Execution Plan
----------------------------------------------------------
Plan hash value: 3557914527
--------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
--------------------------------------------------------------------------------------------
| 0 | SELECT
STATEMENT | |
989 | 110K| 360
(1)| 00:00:05 | | |
| 1 | PARTITION RANGE ALL| |
989 | 110K| 360
(1)| 00:00:05 | 1 | 8 |
|* 2 | TABLE ACCESS FULL | T |
989 | 110K| 360
(1)| 00:00:05 | 1 | 8 |
--------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("BEGIN_DATE">=TO_DATE('
2016-04-01 00:00:00', 'syyyy-mm-dd
hh24:mi:ss')
AND "BEGIN_DATE"<=TO_DATE(' 2016-04-15 00:00:00', 'syyyy-mm-dd
hh24:mi:ss'))
The
table is partitioned by end_date and the above query doesn’t have any filters/predicates
on the end_date column so we scanned all the partitions to answer this query.
But
if we could provide some information about the data representation, then the
optimizer can make use of them during optimization to yield better plans.
demo@ORA11G> alter table t add constraint t_check
2 check(begin_date <= end_date);
Table altered.
demo@ORA11G> alter table t modify begin_date not null;
Table altered.
demo@ORA11G> alter table t modify end_date not null;
Table altered.
So
I have provided the information via the check constraint saying that begin_date and end_date are mandatory columns and begin_date should always be less than or equal to end_date column.
With
these metadata in place, executing the above query would produce the plan like
this.
demo@ORA11G> select *
2 from t
3 where begin_date between
4 to_date('01-apr-2016','dd-mon-yyyy') and
5 to_date('15-apr-2016','dd-mon-yyyy') ;
Execution Plan
----------------------------------------------------------
Plan hash value: 1571388083
-------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
-------------------------------------------------------------------------------------------------
| 0 | SELECT
STATEMENT | |
989 | 110K| 7
(0)| 00:00:01 | | |
| 1 | PARTITION RANGE ITERATOR| |
989 | 110K| 7
(0)| 00:00:01 | 7 |
8 |
|* 2 | TABLE ACCESS FULL | T
| 989 | 110K|
7 (0)| 00:00:01 | 7 | 8 |
-------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 -
filter("BEGIN_DATE">=TO_DATE(' 2016-04-01 00:00:00', 'syyyy-mm-dd
hh24:mi:ss')
AND
"BEGIN_DATE"<=TO_DATE(' 2016-04-15 00:00:00', 'syyyy-mm-dd
hh24:mi:ss') AND
"END_DATE">=TO_DATE(' 2016-04-01 00:00:00',
'syyyy-mm-dd hh24:mi:ss'))
With
the presence of check and not null constraint, the optimizer effectively added the
predicate on end_date column and
resulted in scanning only a subset of partitions.
No comments:
Post a Comment