Home > OS >  Creating n minute intervals per table row
Creating n minute intervals per table row

Time:10-11

I'm trying to get time intervals based on a table.

My source table is something like this:

ID OTHER_DATA TIME_BEG TIME_END DURATION
1 abcd 10:00 11:00 15
2 xyzt 16:00 17:00 30

Desired output:

ID OTHER_DATA ITVL_BEG ITVL_END
1 abcd 10:00 10:15
1 abcd 10:15 10:30
1 abcd 10:30 10:45
1 abcd 10:45 11:00
2 xyzt 16:00 16:30
2 xyzt 16:30 17:00

TIME_BEG and TIME_END are VARCHAR columns but I also have them as DAY TO SECOND INTERVAL which are not shown here (TIME_BEG_INT and TIME_END_INT respectively).

Basically I need to duplicate every row TRUNC (EXTRACT (DAY FROM 24 * 60 * (TIME_END_INT - TIME_BEG_INT)) / DURATION) times and add this*DURATION to my dates, in one SQL.

Any help is appreciated.

CodePudding user response:

If you are using intervals:

CREATE TABLE table_name (ID, OTHER_DATA, TIME_BEG, TIME_END, DURATION) AS
SELECT 1, 'abcd', INTERVAL '10:00' HOUR TO MINUTE, INTERVAL '11:00' HOUR TO MINUTE, INTERVAL '15' MINUTE FROM DUAL UNION ALL
SELECT 2, 'xyzt', INTERVAL '16:00' HOUR TO MINUTE, INTERVAL '17:00' HOUR TO MINUTE, INTERVAL '30' MINUTE FROM DUAL;

Then you can use:

WITH range (ID, OTHER_DATA, TIME_BEG, TIME_INT_END, TIME_END, DURATION) AS (
  SELECT ID,
         OTHER_DATA,
         TIME_BEG,
         LEAST(time_beg   duration, time_end),
         TIME_END,
         DURATION
  FROM   table_name
UNION ALL
  SELECT ID,
         OTHER_DATA,
         TIME_INT_END,
         LEAST(time_int_end   duration, time_end),
         TIME_END,
         DURATION
  FROM   range
  WHERE  time_int_end < time_end
)
SEARCH DEPTH FIRST BY id SET id_order
SELECT ID,
       OTHER_DATA,
       TIME_BEG AS itvl_beg,
       TIME_INT_END AS itvl_end
FROM   range;

Which outputs:

ID OTHER_DATA ITVL_BEG ITVL_END
1 abcd 000000000 10:00:00.000000000 000000000 10:15:00.000000000
1 abcd 000000000 10:15:00.000000000 000000000 10:30:00.000000000
1 abcd 000000000 10:30:00.000000000 000000000 10:45:00.000000000
1 abcd 000000000 10:45:00.000000000 000000000 11:00:00.000000000
2 xyzt 000000000 16:00:00.000000000 000000000 16:30:00.000000000
2 xyzt 000000000 16:30:00.000000000 000000000 17:00:00.000000000

If you have the values as strings then you can convert them to intervals first:

CREATE TABLE table_name (ID, OTHER_DATA, TIME_BEG, TIME_END, DURATION) AS
SELECT 1, 'abcd', '10:00', '11:00', 15 FROM DUAL UNION ALL
SELECT 2, 'xyzt', '16:00', '17:00', 30 FROM DUAL;
WITH data(ID, OTHER_DATA, TIME_BEG, TIME_END, DURATION) AS (
  SELECT ID,
         OTHER_DATA,
         TO_DSINTERVAL('0 '||TIME_BEG||':00'),
         TO_DSINTERVAL('0 '||TIME_END||':00'),
         NUMTODSINTERVAL(DURATION, 'MINUTE')
  FROM   table_name
),
range (ID, OTHER_DATA, TIME_BEG, TIME_INT_END, TIME_END, DURATION) AS (
  SELECT ID,
         OTHER_DATA,
         TIME_BEG,
         LEAST(time_beg   duration, time_end),
         TIME_END,
         DURATION
  FROM   data
UNION ALL
  SELECT ID,
         OTHER_DATA,
         TIME_INT_END,
         LEAST(time_int_end   duration, time_end),
         TIME_END,
         DURATION
  FROM   range
  WHERE  time_int_end < time_end
)
SEARCH DEPTH FIRST BY id SET id_order
SELECT ID,
       OTHER_DATA,
       TIME_BEG AS itvl_beg,
       TIME_INT_END AS itvl_end
FROM   range;

db<>fiddle here

CodePudding user response:

This is the classic solution based on the pre-interval DATE calculation

with time_int(ID, OTHER_DATA, ITVL_BEG, ITVL_END, DURATION, TIME_END) as (
select 
 ID, OTHER_DATA, TIME_BEG, 
 to_char(to_date(time_beg,'HH24:mi') duration/(24*60),'HH24:mi'), 
 DURATION, TIME_END 
from tab
union all
select 
 ID, OTHER_DATA, ITVL_END, 
 to_char(to_date(ITVL_END,'HH24:mi') duration/(24*60),'HH24:mi'), 
 DURATION, TIME_END 
from time_int
where ITVL_END <= TIME_END
)
select 
   ID, OTHER_DATA, ITVL_BEG, ITVL_END
from time_int
order by 1,3;

        ID OTHE ITVL_ ITVL_
---------- ---- ----- -----
         1 abcd 10:00 10:15
         1 abcd 10:15 10:30
         1 abcd 10:30 10:45
         1 abcd 10:45 11:00
         1 abcd 11:00 11:15
         2 xyzt 16:00 16:30
         2 xyzt 16:30 17:00
         2 xyzt 17:00 17:30

A recursive CTE is used with the step based on the following calculation to get the next interval boundary (assuming that your time is stored as VARCHAR2)

to_char(to_date(time_beg,'HH24:mi') duration/(24*60),'HH24:mi')

Note that you may convert 10:00 to date and Oracle provides the default missing day, month and year, that you do not need because after increasing by duration you converts back to a HH24:MI string.

  • Related