Home > other >  Procedure/Function to find and fill empty time spots
Procedure/Function to find and fill empty time spots

Time:03-30

I have a table named DAILY_EVENTS and it holds data like this:

EVENT_ID   | EVENT_TYPE       | From_date           | To_date
----------- ------------ ----- --------------------- ---------------------
3          | ACCIDENT         | 2013-01-05 01:14:47 | 2013-01-05 01:14:55
6          | TERRORISM        | 2013-01-05 22:55:20 | 2013-01-05 22:56:00
8          | NATURAL DISASTER | 2013-01-05 16:05:30 | 2013-01-05 16:05:39

I need to find empty time spots to insert more rows for this day with these conditions:

a) Time ranges cannot be superimposed. i.e. new rows From_date and To_date cannot be in the range between 01:14:47 to 01:14:55, or 22:55:20 to 22:56:00 and so on.

b) 10 seconds between From_date time and To_date time i.e 01:14:00 - 01:14:10

c) minimum of 2 seconds between the To_date of the event previously inserted and the from_date of the current event i.e. if First row is 01:14:00 - 01:14:10, then second row must starts minimum at 01:14:12, and the To_Date will be 01:14:22

A summary of 2 new rows (last ones) with allowed data could be these:

EVENT_ID   | EVENT_TYPE       | From_date           | To_date
----------- ------------ ----- --------------------- ---------------------
3          | ACCIDENT         | 2013-01-05 01:14:47 | 2013-01-05 01:14:55
6          | TERRORISM        | 2013-01-05 22:55:20 | 2013-01-05 22:56:00
8          | NATURAL DISASTER | 2013-01-05 16:05:30 | 2013-01-05 16:05:39
3          | ACCIDENT         | 2013-01-05 00:00:00 | 2013-01-05 00:00:10
6          | ACCIDENT         | 2013-01-05 00:00:12 | 2013-01-05 00:00:22

I'm kinda blocked with this requirement, I don't know how can I start with, any idea?

CodePudding user response:

You can generate all the gaps using:

SELECT LAG(to_date, 1, TRUNC(from_date)) OVER (ORDER BY From_date) AS gap_start,
       from_date AS gap_end
FROM   daily_events
WHERE  to_date   > DATE '2013-01-05'
AND    from_date < DATE '2013-01-05'   INTERVAL '1' DAY
UNION ALL
SELECT COALESCE(MAX(to_date), DATE '2013-01-05'),
       DATE '2013-01-05'   INTERVAL '1' DAY
FROM   daily_events
WHERE  to_date   > DATE '2013-01-05'
AND    from_date < DATE '2013-01-05'   INTERVAL '1' DAY;

Which, for the sample data:

CREATE TABLE daily_events (EVENT_ID, EVENT_TYPE, From_date, To_date) AS
SELECT 3, 'ACCIDENT',         DATE '2013-01-05'   INTERVAL '01:14:47' HOUR TO SECOND, DATE '2013-01-05'   INTERVAL '01:14:55' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 6, 'TERRORISM',        DATE '2013-01-05'   INTERVAL '22:55:20' HOUR TO SECOND, DATE '2013-01-05'   INTERVAL '22:56:00' HOUR TO SECOND FROM DUAL UNION ALL
SELECT 8, 'NATURAL DISASTER', DATE '2013-01-05'   INTERVAL '16:05:30' HOUR TO SECOND, DATE '2013-01-05'   INTERVAL '16:05:39' HOUR TO SECOND FROM DUAL;

Outputs:

GAP_START GAP_END
2013-01-05 00:00:00 2013-01-05 01:14:47
2013-01-05 01:14:55 2013-01-05 16:05:30
2013-01-05 16:05:39 2013-01-05 22:55:20
2013-01-05 22:56:00 2013-01-06 00:00:00

Then you just need to pick a 10-second range within those gaps.


You can generate all the possible non-overlapping 10-second intervals, starting from 2-seconds after the end of the previous interval, using a recursive query:

WITH gaps (gap_start, gap_end) AS (
  SELECT LAG(to_date, 1, TRUNC(from_date)) OVER (ORDER BY From_date),
         from_date
  FROM   daily_events
  WHERE  to_date   > DATE '2013-01-05'
  AND    from_date < DATE '2013-01-05'   INTERVAL '1' DAY
UNION ALL
  SELECT COALESCE(MAX(to_date), DATE '2013-01-05'),
         DATE '2013-01-05'   INTERVAL '1' DAY
  FROM   daily_events
  WHERE  to_date   > DATE '2013-01-05'
  AND    from_date < DATE '2013-01-05'   INTERVAL '1' DAY
),
intervals (gap_start, gap_end, int_start, int_end) AS (
  SELECT gap_start,
         gap_end,
         gap_start   INTERVAL '2' SECOND,
         gap_start   INTERVAL '12' SECOND
  FROM   gaps
  WHERE  gap_start   INTERVAL '14' SECOND <= gap_end
UNION ALL
  SELECT gap_start,
         gap_end,
         int_end   INTERVAL '2' SECOND,
         int_end   INTERVAL '12' SECOND
  FROM   intervals
  WHERE  int_end   INTERVAL '14' SECOND <= gap_end
)
SEARCH DEPTH FIRST BY gap_start SET int_order
SELECT int_start, int_end
FROM   intervals;

Which, for the sample data, outputs:

INT_START INT_END
2013-01-05 00:00:02 2013-01-05 00:00:12
2013-01-05 00:00:14 2013-01-05 00:00:24
2013-01-05 00:00:26 2013-01-05 00:00:36
... ...
2013-01-05 01:14:02 2013-01-05 01:14:12
2013-01-05 01:14:14 2013-01-05 01:14:24
2013-01-05 01:14:26 2013-01-05 01:14:36
2013-01-05 01:14:57 2013-01-05 01:15:07
2013-01-05 01:15:09 2013-01-05 01:15:19
2013-01-05 01:15:21 2013-01-05 01:15:31
... ...
2013-01-05 16:04:45 2013-01-05 16:04:55
2013-01-05 16:04:57 2013-01-05 16:05:07
2013-01-05 16:05:09 2013-01-05 16:05:19
2013-01-05 16:05:41 2013-01-05 16:05:51
2013-01-05 16:05:53 2013-01-05 16:06:03
2013-01-05 16:06:05 2013-01-05 16:06:15
... ...
2013-01-05 22:56:26 2013-01-05 22:56:36
2013-01-05 22:56:38 2013-01-05 22:56:48
2013-01-05 22:56:50 2013-01-05 22:57:00
2013-01-05 22:57:02 2013-01-05 22:57:12
2013-01-05 22:57:14 2013-01-05 22:57:24
2013-01-05 22:57:26 2013-01-05 22:57:36
... ...
2013-01-05 23:59:14 2013-01-05 23:59:24
2013-01-05 23:59:26 2013-01-05 23:59:36
2013-01-05 23:59:38 2013-01-05 23:59:48

db<>fiddle here

  • Related