Skip to Main Content
  • Questions
  • INCREMENT fractional part of timestamp

Breadcrumb

Question and Answer

Chris Saxon

Thanks for the question, Paul.

Asked: January 31, 2022 - 7:21 am UTC

Last updated: January 31, 2022 - 11:36 am UTC

Version: 19.2

Viewed 1000+ times

You Asked

I have a table, which contains a timestamp. I want each row an INTERVAL apart. In my example below I am using a 15 minutes interval

My first solution appears to work perfectly except that the fractional part of the timestamp is always .000000, which I expect but don't want. I'd like it to contain some other numbers.

In my second attempt I'm trying to INCREMENT the fractional part of the timestamp by .100000 the problem with this solution is if I'm creating many rows (1344) in my secomd example, the seconds part of the timestamp gets incremented by 1 second after 10 rows are inserted. See the second solution below. I don't want that either.

My third solution works
and I get the desired results but my QA team is finding this solution cumbersome as they need to adjust the fraction and the MOD number in order not to INCREMENT the seconds. For example, let's say they want to INCREMENT by .25 or .150, etc for the fractional part of the timestamp that means readjusting VALUES in two places, which they find cumbersome.


Lastly, I thought perhaps a regexp_replace solution would work where I could chop off the integer part of the second solution if the number of rows being generated would change the value of the seconds. For example, if 1.100000 is being generated I would like the regex to only keep the fractional part ie .100000 and add that part only. That attempt failed with an error. See attempt below.

Is there a method I can use like regex to remove the whole number so the seconds will not change but keep the fractional part of the timestamp. I've been struggling to get this to work but have been unsuccessful.

Can you add some insight on why this is failing and provide a working example for attempt number 4.

Below is my code and attempts. Thanks in advance for your time and expertise.

Attempt #1 fractional part always .000000

CREATE TABLE t3 (
seq_num NUMBER  GENERATED BY DEFAULT AS IDENTITY (START WITH 1) NOT NULL,
dt TIMESTAMP  );
/

INSERT into t3 (dt)
with dt (dt, interv) as (
select timestamp '2022-01-01 00:00:00', 
numtodsinterval(15,'MINUTE') from dual
union all
select dt.dt + interv, interv from dt
where dt.dt + interv < date '2022-01-15')
select dt from dt;
/

SELECT * FROM T3 ORDER BY SEQ_NUM 

SEQ_NUM    DT
1    01-JAN-22 12.00.00.000000 AM
2    01-JAN-22 12.15.00.000000 AM
3    01-JAN-22 12.30.00.000000 AM
4    01-JAN-22 12.45.00.000000 AM
5    01-JAN-22 01.00.00.000000 AM
6    01-JAN-22 01.15.00.000000 AM
…
...
1342    14-JAN-22 11.15.00.000000 PM
1343    14-JAN-22 11.30.00.000000 PM
1344    14-JAN-22 11.45.00.000000 PM


Attempt #2 notice the seconds change at seq_num 1355. It went from :00 to :01. I don't want the seconds to change.

TRUNCATE TABLE T3;
/

INSERT into t3 (dt)
with dt (dt, interv) as (
select timestamp '2022-01-01 00:00:00', 
numtodsinterval(15,'MINUTE') +  
numtodsinterval(  (rownum * .100000), 'SECOND')  from dual
union all
select dt.dt + interv, interv from dt
where dt.dt + interv < date '2022-01-15')
select dt from dt;
/

SELECT * FROM T3 ORDER BY SEQ_NUM 

SEQ_NUM    DT
1345    01-JAN-22 12.00.00.000000 AM
1346    01-JAN-22 12.15.00.100000 AM
1347    01-JAN-22 12.30.00.200000 AM
1348    01-JAN-22 12.45.00.300000 AM
1349    01-JAN-22 01.00.00.400000 AM
1350    01-JAN-22 01.15.00.500000 AM
1351    01-JAN-22 01.30.00.600000 AM
1352    01-JAN-22 01.45.00.700000 AM
1353    01-JAN-22 02.00.00.800000 AM
1354    01-JAN-22 02.15.00.900000 AM
1355    01-JAN-22 02.30.01.000000 AM
1356    01-JAN-22 02.45.01.100000 AM
…
…

Attempt #3 successful but awkward for testing

TRUNCATE TABLE T3;
/

INSERT /*+ APPEND */ into t3 (dt)
SELECT TIMESTAMP '2022-01-01 00:00:00'
         + (LEVEL - 1) * INTERVAL '5' MINUTE
         + MOD(LEVEL - 1, 10) * INTERVAL '0.1' SECOND
FROM   DUAL
CONNECT BY
       TIMESTAMP '2022-01-01 00:00:00'
         + (LEVEL - 1) * INTERVAL '5' MINUTE
         + MOD(LEVEL - 1, 10) * INTERVAL '0.1' SECOND < DATE '2022-01-15';
/

Looking to get something like this to work.

TRUNCATE TABLE T3;
/

INSERT into t3 (dt)
with dt (dt, interv) as (
select timestamp '2022-01-01 00:00:00', 
numtodsinterval(15,'MINUTE') +  
regexp_replace(
numtodsinterval(  (rownum * .100000), 'SECOND'), '[^.]+\.(.*)$', '0.\1') from dual
union all
select dt.dt + interv, interv from dt
where dt.dt + interv < date '2022-01-15')
select dt from dt;
/




and Chris said...

I'm a little unclear exactly what you're trying to achieve here - you want to increase the minutes/seconds by a fixed offset, but have a "random" value for the fractional seconds?

Anyway one way to avoid duplicating the values is to select them all first in a subquery. Then reference these:

with vals as (
  select timestamp '2022-01-01 00:00:00' start_date,
         timestamp '2022-01-01 03:00:00' end_date,
         interval '15' minute time_increment,
         interval '0.1' second fraction,
         10 second_mod
  from   dual
)
  select start_date 
      + (level - 1) * time_increment
      + mod(level - 1, second_mod) * fraction ts
  from   vals
  connect by start_date 
      + (level - 1) * time_increment
      + mod(level - 1, second_mod) * fraction < end_date;
      
TS                            
------------------------------
01-JAN-2022 00.00.00.000000000
01-JAN-2022 00.15.00.100000000
01-JAN-2022 00.30.00.200000000
01-JAN-2022 00.45.00.300000000
01-JAN-2022 01.00.00.400000000
01-JAN-2022 01.15.00.500000000
01-JAN-2022 01.30.00.600000000
01-JAN-2022 01.45.00.700000000
01-JAN-2022 02.00.00.800000000
01-JAN-2022 02.15.00.900000000
01-JAN-2022 02.30.00.000000000
01-JAN-2022 02.45.00.100000000


Or you could make all the values bind variables:

  select to_timestamp ( :start_date, 'yyyy-mm-dd hh24:mi:ss' )
      + (level - 1) * numtodsinterval ( :time_increment, 'minute' )
      + mod(level - 1, :second_mod ) * numtodsinterval ( :fraction, 'second' ) ts
  from   dual
  connect by to_timestamp ( :start_date, 'yyyy-mm-dd hh24:mi:ss' )
      + (level - 1) * numtodsinterval ( :time_increment, 'minute' )
      + mod(level - 1, :second_mod ) * numtodsinterval ( :fraction, 'second' ) < 
        to_timestamp ( :end_date, 'yyyy-mm-dd hh24:mi:ss' );


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

More to Explore

SQL

The Oracle documentation contains a complete SQL reference.