Как разбить диапазон времени на две записи по заданному моменту времени?

Имеется таблица, в ней есть два поля: date_from и date_to.
Если диапазон времени проходит через определенную точку, например point, то запись с данным временем необходимо дублировать, а диапазон делить на два.

Например, если:

date_from = 23.04.2020 22:26:00
date_to = 23.04.2020 23:59:00
point = 23:00:00

, то у нас вместо одной записи появляется дубликат с новыми диапазонами времени:

1 from 23.04.2020 22:26:00 to 23.04.2020 23:00:00
2 from 23.04.2020 23:00:01 to 23.04.2020 23:59:00

Больше данных в файле Google Docs.

Не пониманию, как сделать проверку вхождения точки в данный интервал. Но самое страшное, как проверить вхождение, если point = 00:00:00 (полночь)?

Проверка на SQL и PL/SQL.


Ответы (2 шт):

Автор решения: 0xdb

Попробуйте SQL запросом, вот так:

with params as (
    select 'dd.mm.yyyy hh24:mi:ss' fmt, '00:00:00' point, 1/86400 sec from dual
), 
testdata (date_from, date_to) as (
    select to_date ('05.04.2020 18:53:23', fmt), to_date ('05.04.2020 19:27:23', fmt) from params union all 
    select to_date ('05.04.2020 22:50:09', fmt), to_date ('05.04.2020 23:20:09', fmt) from params union all 
    select to_date ('05.04.2020 23:30:29', fmt), to_date ('06.04.2020 00:00:29', fmt) from params union all 
    select to_date ('06.04.2020 01:40:33', fmt), to_date ('06.04.2020 02:15:33', fmt) from params 
)
select 
    case level when 1 then date_from else point+1*sec end date_from,
    case level when 1 then point     else date_to end date_to     
from (
    select * from (
        select 
            testdata.*, params.sec, 
            to_date (to_char (date_to,'dd.mm.yyyy')||point, fmt) point
        from testdata cross join params
    ) where point between date_from and date_to
) connect by level <= 2;

Выведет поделенный на две запсиси диапазон, который проходит через полночь:

DATE_FROM           DATE_TO            
------------------- -------------------
2020-04-05 23:30:29 2020-04-06 00:00:00
2020-04-06 00:00:01 2020-04-06 00:00:29
→ Ссылка
Автор решения: 0xdb

Решение на PL/SQL выглядит более понятно:

create or replace type dateRange is object (dtFrom date, dtTo date)
/
create or replace type dateRanges is table of dateRange
/
create or replace function splitDateRangeByTimePoint (dr dateRange, tp char) return dateRanges is
    fmt constant char(21) := 'dd.mm.yyyy hh24:mi:ss';
    sec constant number := 1/86400;
    point date := to_date (to_char (dr.dtTo,'dd.mm.yyyy')||tp, fmt);
    ret dateRanges;
begin  
    return case 
        when point >= dr.dtFrom and point < dr.dtTo then
        dateRanges (
            dateRange (dr.dtFrom, point), 
            dateRange (point+1*sec, dr.dtTo))
        else dateRanges () 
        end; 
end;
/

Запуск с тестовыми данными из этого ответа даст ожидаемый результат:

with testdata (date_from, date_to) as ( /* здесь тестовые данные */ )
select splited.* 
from testdata td
cross join splitDateRangeByTimePoint (
    dr=>dateRange (date_from, date_to), tp=>'00:00:00') splited;

DTFROM              DTTO               
------------------- -------------------
2020-04-05 23:30:29 2020-04-06 00:00:00
2020-04-06 00:00:01 2020-04-06 00:00:29
→ Ссылка