Skip to Main Content
  • Questions
  • Timestamp + interval arithmetics fails for precision over 6

Breadcrumb

Question and Answer

Connor McDonald

Thanks for the question, Michal.

Asked: May 15, 2017 - 4:35 pm UTC

Last updated: May 15, 2017 - 7:52 pm UTC

Version: 11g

Viewed 1000+ times

You Asked

Hi team,
I have a problem with adding/subtracting Interval datatype to Timestamp datatype, when precision of the Interval is between 7 and 9.
It seems that it doesn't work correctly, see results 'EQ' (equals=wrong) in below example.

The second query is showing that PL/SQL works well in this aspect.

Test case:
select * from v$version;
BANNER                                                                         
--------------------------------------------------------------------------------
Oracle Database 11g Express Edition Release 11.2.0.2.0 - 64bit Production       
PL/SQL Release 11.2.0.2.0 - Production                                          
CORE 11.2.0.2.0 Production                                                        
TNS for 64-bit Windows: Version 11.2.0.2.0 - Production                         
NLSRTL Version 11.2.0.2.0 - Production                                          

select 
to_char(to_timestamp('1.1.2017 00:00:00.000000001','dd.mm.yyyy hh24:mi:ss.ff')+interval '0.000001' second,'dd.mm.yyyy hh24:mi:ss.ff') as val_p6,
case when to_timestamp('1.1.2017 00:00:00.000000001','dd.mm.yyyy hh24:mi:ss.ff')+interval '0.000001' second = to_timestamp('1.1.2017 00:00:00.000000001','dd.mm.yyyy hh24:mi:ss.ff') then 'EQ' else 'NE' end as equals_p6,
to_char(to_timestamp('1.1.2017 00:00:00.000000001','dd.mm.yyyy hh24:mi:ss.ff')+interval '0.0000001' second,'dd.mm.yyyy hh24:mi:ss.ff') as val_p7,
case when to_timestamp('1.1.2017 00:00:00.000000001','dd.mm.yyyy hh24:mi:ss.ff')+interval '0.0000001' second = to_timestamp('1.1.2017 00:00:00.000000001','dd.mm.yyyy hh24:mi:ss.ff') then 'EQ' else 'NE' end as equals_p7,
to_char(to_timestamp('1.1.2017 00:00:00.000000001','dd.mm.yyyy hh24:mi:ss.ff')+interval '0.000000001' second,'dd.mm.yyyy hh24:mi:ss.ff') as val_p9,
case when to_timestamp('1.1.2017 00:00:00.000000001','dd.mm.yyyy hh24:mi:ss.ff')+interval '0.000000001' second = to_timestamp('1.1.2017 00:00:00.000000001','dd.mm.yyyy hh24:mi:ss.ff') then 'EQ' else 'NE' end as equals_p9
from dual;
VAL_P6                        EQ VAL_P7                        EQ VAL_P9                        EQ
----------------------------- -- ----------------------------- -- ----------------------------- --
01.01.2017 00:00:00.000001001 NE 01.01.2017 00:00:00.000000001 EQ 01.01.2017 00:00:00.000000001 EQ

set serveroutput on
declare
v_sql varchar2(2);
v_plsql varchar2(2);
begin
v_plsql := case when to_timestamp('1.1.2017 00:00:00.000000001','dd.mm.yyyy hh24:mi:ss.ff')+interval '0.000000001' second = to_timestamp('1.1.2017 00:00:00.000000001','dd.mm.yyyy hh24:mi:ss.ff') then 'EQ' else 'NE' end;
execute immediate q'[select case when to_timestamp('1.1.2017 00:00:00.000000001','dd.mm.yyyy hh24:mi:ss.ff')+interval '0.000000001' second = to_timestamp('1.1.2017 00:00:00.000000001','dd.mm.yyyy hh24:mi:ss.ff') then 'EQ' else 'NE' end from dual]' into v_sql;
dbms_output.put_line('v_sql='||to_char(v_sql)||', v_plsql='||to_char(v_plsql));
end;
/

PL/SQL procedure successfully completed.

v_sql=EQ, v_plsql=NE


Could you, please, give me a feedback concerning this issue?

and Connor said...

It is a default precision issue. If your platform has a default precision of 6, then you get 6 when using just a simple interval command. Hence 7 digits gets rounded down to 6.

Examples:

SQL> select
  2    to_char(to_timestamp('1.1.2017 00:00:00.000000001','dd.mm.yyyy hh24:mi:ss.ff')+
  3       interval '0.000000001' second,'dd.mm.yyyy hh24:mi:ss.ff') as c1
  4  from dual;

C1
-----------------------------
01.01.2017 00:00:00.000000001  <== looks wrong

SQL>
SQL> select
  2    to_char(to_timestamp('1.1.2017 00:00:00.000000001','dd.mm.yyyy hh24:mi:ss.ff')+
  3       interval '0.000000001' second(2,9),'dd.mm.yyyy hh24:mi:ss.ff') as c1
  4  from dual;

C1
-----------------------------
01.01.2017 00:00:00.000000002  <==== correct


Rating

  (1 rating)

Is this answer out of date? If it is, please let us know via a Comment

Comments

problem solved

Michal, May 15, 2017 - 10:18 pm UTC

Thank you for your reply, it fully solved my problem.

I was searching in Oracle Doc at the wrong place (Data Types), the correct one is Literals.
A bit confusing was also that PL/SQL behaves differently from SQL in a matter of default interval fractional_precision. But it is not a problem when I know the correct precision-spec syntax now.

Maybe for others for full clarification - it is not easy to understand from documentation what is the first number (2,n) related to. It is called leading_precision there, I have realized that it means precision of number of days in our case (e.g. 0 means value up to 24*60*60-1 before decimal point).
Another possible syntax of sub-second interval, without explicit leading_precision specificaton, is:
INTERVAL '00:00.000000001' MINUTE TO SECOND(9)

More to Explore

PL/SQL demos

Check out more PL/SQL tutorials on our LiveSQL tool.

PL/SQL docs

PL/SQL reference manual from the Oracle documentation library