Если вы используете более новую версию Oracle (12 c и выше), возможно, стоит использовать MATCH_RECOGNIZE (сопоставление с образцом). В следующем запросе мы определяем 2 шаблона:
{1} несколько дней, с начальной и конечной датами. Этот шаблон может содержать «ненадежные» даты, например, 01/02/2000 в период с 01/05/2000 по 01/15/2000 для блока 40.
{2} один день: это происходит при запуске "и" конец "в один и тот же день. В предложении MEASURES мы выбираем все нужные нам столбцы и COALESCE их в предложении SELECT (документация MATCH_RECOGNIZE здесь ).
Таблица
create table fileandunit( FILE_, UNIT, DATE_, ID_SEQUENCE )
as
select 10, 34, to_date( '02-JAN-2000', 'DD-MON-YYYY'), 10 from dual union all
select 10, 34, to_date( '05-JAN-2000', 'DD-MON-YYYY'), 11 from dual union all
select 10, 40, to_date( '05-JAN-2000', 'DD-MON-YYYY'), 12 from dual union all
select 10, 40, to_date( '02-JAN-2000', 'DD-MON-YYYY'), 13 from dual union all
select 10, 40, to_date( '02-JAN-2000', 'DD-MON-YYYY'), 14 from dual union all
select 10, 40, to_date( '15-JAN-2000', 'DD-MON-YYYY'), 15 from dual union all
select 10, 34, to_date( '16-JAN-2000', 'DD-MON-YYYY'), 16 from dual union all
select 10, 70, to_date( '17-JAN-2000', 'DD-MON-YYYY'), 17 from dual union all
select 10, 70, to_date( '28-JAN-2000', 'DD-MON-YYYY'), 18 from dual ;
Данные и шаблоны
select * from fileandunit order by id_sequence ;
FILE_ UNIT DATE_ ID_SEQUENCE
10 34 02-JAN-00 10 -- start
10 34 05-JAN-00 11 -- end
10 40 05-JAN-00 12 -- start
10 40 02-JAN-00 13 -- iffy
10 40 02-JAN-00 14 -- iffy
10 40 15-JAN-00 15 -- end
10 34 16-JAN-00 16 -- single day
10 70 17-JAN-00 17 -- start
10 70 28-JAN-00 18 -- end
Запрос
select
coalesce( RP.m_file, RP.s_file ) file_
, coalesce( RP.m_unit, RP.s_unit ) unit_
, coalesce( ( RP.m_end - RP.m_start ), 1 ) days_
from fileandunit
match_recognize(
partition by file_ order by id_sequence
measures
enddt.file_ as m_file -- m_: for multiple days
, enddt.unit as m_unit
, startdt.date_ as m_start
, enddt.date_ as m_end
, singledt.file_ as s_file -- s_: single day
, singledt.unit as s_unit
, singledt.date_ as s_date
one row per match
pattern ( ( startdt iffydt* enddt ) | singledt ) -- multiple days (or) single day
define
startdt as ( prev( date_ ) <= date_ or prev( date_ ) is null )
and ( prev( unit ) <> unit or prev( unit ) is null )
--
, enddt as ( next( date_ ) >= date_ or next( date_ ) is null )
and ( next( unit ) <> unit or next( unit ) is null )
--
, iffydt as ( prev( date_ ) >= date_ ) -- detect incorrect dates inside a multiple day block
and ( prev( unit ) = unit )
--
, singledt as ( prev( date_ ) = date_ - 1 and next( date_ ) = date_ + 1 )
and ( prev( unit ) <> unit and next( unit ) <> unit )
) RP ;
Результат
FILE_ UNIT_ DAYS_
10 34 3
10 40 10
10 34 1
10 70 11
Проверено с Oracle 18 c. DBfiddle здесь .