Oracle Partition Range by Date Precision

Viewed 78

My understanding is that there is no fractional second for a Date data type. If that is true, then why do the three queries below not all have Pstart=6 and why do they not all have no filter predicate?

That is, for a Date data type is it true that the following two are logically equivalent?

:x > to_date('20210605 00:00:00','YYYYMMDD HH24:MI:SS')

:x >= to_date('20210605 00:00:01','YYYYMMDD HH24:MI:SS')

Oracle 12.1 Enterprise Edition.

Thanks in advance.

Setup: (though this is probably a bit too much for this question, but I was using this setup for other tests)

drop table p;

create table p
  (
    grp CHAR(3 byte)
    , dt DATE
    , payment_amount NUMBER
  )
  nocompress 
  tablespace data
  partition by LIST (grp)
  subpartition by range (dt)
  ( partition P_P_A values ('A')
    ( subpartition P_P_A_20210410 values less than (to_date('20210410 00:00:01','YYYYMMDD HH24:MI:SS'))
     ,subpartition P_P_A_20210424 values less than (to_date('20210424 00:00:01','YYYYMMDD HH24:MI:SS'))
     ,subpartition P_P_A_20210508 values less than (to_date('20210508 00:00:01','YYYYMMDD HH24:MI:SS'))
     ,subpartition P_P_A_20210522 values less than (to_date('20210522 00:00:01','YYYYMMDD HH24:MI:SS'))
     ,subpartition P_P_A_20210605 values less than (to_date('20210605 00:00:01','YYYYMMDD HH24:MI:SS'))
     ,subpartition P_P_A_20210619 values less than (to_date('20210619 00:00:01','YYYYMMDD HH24:MI:SS'))
     ,subpartition P_P_A_20210703 values less than (to_date('20210703 00:00:01','YYYYMMDD HH24:MI:SS'))
    )
   ,partition P_P_B values ('B')
    ( subpartition P_P_B_20210410 values less than (to_date('20210410 00:00:01','YYYYMMDD HH24:MI:SS'))
     ,subpartition P_P_B_20210424 values less than (to_date('20210424 00:00:01','YYYYMMDD HH24:MI:SS'))
     ,subpartition P_P_B_20210508 values less than (to_date('20210508 00:00:01','YYYYMMDD HH24:MI:SS'))
     ,subpartition P_P_B_20210522 values less than (to_date('20210522 00:00:01','YYYYMMDD HH24:MI:SS'))
     ,subpartition P_P_B_20210605 values less than (to_date('20210605 00:00:01','YYYYMMDD HH24:MI:SS'))
     ,subpartition P_P_B_20210619 values less than (to_date('20210619 00:00:01','YYYYMMDD HH24:MI:SS'))
     ,subpartition P_P_B_20210703 values less than (to_date('20210703 00:00:01','YYYYMMDD HH24:MI:SS'))
    )
   ,partition P_P_C values ('C')
    ( subpartition P_P_C_20210410 values less than (to_date('20210410 00:00:01','YYYYMMDD HH24:MI:SS'))
     ,subpartition P_P_C_20210424 values less than (to_date('20210424 00:00:01','YYYYMMDD HH24:MI:SS'))
     ,subpartition P_P_C_20210508 values less than (to_date('20210508 00:00:01','YYYYMMDD HH24:MI:SS'))
     ,subpartition P_P_C_20210522 values less than (to_date('20210522 00:00:01','YYYYMMDD HH24:MI:SS'))
     ,subpartition P_P_C_20210605 values less than (to_date('20210605 00:00:01','YYYYMMDD HH24:MI:SS'))
     ,subpartition P_P_C_20210619 values less than (to_date('20210619 00:00:01','YYYYMMDD HH24:MI:SS'))
     ,subpartition P_P_C_20210703 values less than (to_date('20210703 00:00:01','YYYYMMDD HH24:MI:SS'))
    )
  )
;
insert 
  into p
     ( grp
      ,dt
      ,payment_amount
     )
with "D" as (select /*+ materialize */ 1 from dual connect by level <= 1000)
select case mod(rownum, 3) when 1 then 'A' when 2 then 'B' when 0 then 'C' end
      , to_date('20210410','YYYYMMDD') + 14 * mod(rownum, 7)
      , dbms_random.value(500, 5000)
  from "D", "D"
;
commit;
exec dbms_stats.gather_table_stats(null, 'P');

Query 1 - Strict Inequality

select sum(P.payment_amount) 
  from p "P" 
 where P.grp = 'B'
       and P.dt > to_date('20210605 00:00:00','YYYYMMDD HH24:MI:SS') 
       and P.dt < to_date('20210703 00:00:01','YYYYMMDD HH24:MI:SS')
;  
----------------------------------------------------------------------------------------------------
| id  | Operation                  | name  | rows  | Bytes | cost (%CPU)| time     | Pstart| Pstop |
----------------------------------------------------------------------------------------------------
|   0 | select statement           |       |     1 |    34 |  2152   (1)| 00:00:01 |       |       |
|   1 |  sort AGGREGATE            |       |     1 |    34 |            |          |       |       |
|   2 |   partition LIST single    |       |   111K|  3689K|  2152   (1)| 00:00:01 |   key |   key |
|   3 |    partition range ITERATOR|       |   111K|  3689K|  2152   (1)| 00:00:01 |     5 |     7 |
|*  4 |     table access full      | p     |   111K|  3689K|  2152   (1)| 00:00:01 |       |       |
----------------------------------------------------------------------------------------------------
  
Predicate Information (identified by operation id):
---------------------------------------------------

   4 - filter("P"."DT">to_date(' 2021-06-05 00:00:00', 'syyyy-mm-dd hh24:mi:ss'))

Query 2 - Non-Strict Inequality:

select sum(P.payment_amount) 
  from p "P" 
 where P.grp = 'B'
       and P.dt >= to_date('20210605 00:00:01','YYYYMMDD HH24:MI:SS') 
       and P.dt <= to_date('20210703 00:00:00','YYYYMMDD HH24:MI:SS')
;  
----------------------------------------------------------------------------------------------------
| id  | Operation                  | name  | rows  | Bytes | cost (%CPU)| time     | Pstart| Pstop |
----------------------------------------------------------------------------------------------------
|   0 | select statement           |       |     1 |    34 |  1436   (1)| 00:00:01 |       |       |
|   1 |  sort AGGREGATE            |       |     1 |    34 |            |          |       |       |
|   2 |   partition LIST single    |       |   158K|  5270K|  1436   (1)| 00:00:01 |   key |   key |
|   3 |    partition range ITERATOR|       |   158K|  5270K|  1436   (1)| 00:00:01 |     6 |     7 |
|*  4 |     table access full      | p     |   158K|  5270K|  1436   (1)| 00:00:01 |       |       |
----------------------------------------------------------------------------------------------------
  
Predicate Information (identified by operation id):
---------------------------------------------------

   4 - filter("P"."DT"<=to_date(' 2021-07-03 00:00:00', 'syyyy-mm-dd hh24:mi:ss'))

Query 3 - No Predicate:

select sum(P.payment_amount) 
  from p "P" 
 where P.grp = 'B'
       and P.dt >= to_date('20210605 00:00:01','YYYYMMDD HH24:MI:SS') 
       and P.dt < to_date('20210703 00:00:01','YYYYMMDD HH24:MI:SS')
; 
----------------------------------------------------------------------------------------------------
| id  | Operation                  | name  | rows  | Bytes | cost (%CPU)| time     | Pstart| Pstop |
----------------------------------------------------------------------------------------------------
|   0 | select statement           |       |     1 |    34 |  1436   (1)| 00:00:01 |       |       |
|   1 |  sort AGGREGATE            |       |     1 |    34 |            |          |       |       |
|   2 |   partition LIST single    |       |   158K|  5270K|  1436   (1)| 00:00:01 |   key |   key |
|   3 |    partition range ITERATOR|       |   158K|  5270K|  1436   (1)| 00:00:01 |     6 |     7 |
|   4 |     table access full      | p     |   158K|  5270K|  1436   (1)| 00:00:01 |       |       |
----------------------------------------------------------------------------------------------------

Edit: Addendum to clarify use case and motivation for the above experimental subpartition definition.

The application's current subpartition definitions have the following form.

   ,partition P_P_B values ('B')
    ( subpartition P_P_B_20210410 values less than (to_date('20210411','YYYYMMDD'))
     ,subpartition P_P_B_20210424 values less than (to_date('20210425','YYYYMMDD'))
     ,subpartition P_P_B_20210508 values less than (to_date('20210509','YYYYMMDD'))
     ,subpartition P_P_B_20210522 values less than (to_date('20210523','YYYYMMDD'))
     ,subpartition P_P_B_20210605 values less than (to_date('20210606','YYYYMMDD'))
     ,subpartition P_P_B_20210619 values less than (to_date('20210620','YYYYMMDD'))
     ,subpartition P_P_B_20210703 values less than (to_date('20210704','YYYYMMDD'))
     ,subpartition P_P_B_20210717 values less than (to_date('20210718','YYYYMMDD'))
    )

The application generates queries with predicates as follows.

P.dt between to_date('20210606 00:00:00','YYYYMMDD HH24:MI:SS') 
         and to_date('20210703 23:59:59','YYYYMMDD HH24:MI:SS') 

This causes a filter predicate on the upper end of the range.

 4 - filter("P"."DT"<=to_date(' 2021-07-03 23:59:59', 'syyyy-mm-dd hh24:mi:ss'))

Thus, the confusion as to the seemingly non-equivalence of the following two expressions prompted this post.

P.dt <= to_date('20210703 23:59:59','YYYYMMDD HH24:MI:SS') 

and

P.dt < to_date('20210704 00:00:00','YYYYMMDD HH24:MI:SS') 

This filter predicate can be avoided if the application is modified to generate the predicate as follows.

P.dt >= to_date('20210606 00:00:00','YYYYMMDD HH24:MI:SS') 
and P.dt < to_date('20210703 00:00:00','YYYYMMDD HH24:MI:SS') + 1

Having to do this seems "buggy" or "hackish". Was hoping to get clarification from this community.

0 Answers
Related