Разница в дате при изменении группы по значению - PullRequest
1 голос
/ 11 февраля 2020

У меня есть такая таблица

|---------------------|------------------|---------------------|------------------|
|          FILE       |     UNIT         |      DATE           |    ID SEQUENCE   |
|---------------------|------------------|---------------------|------------------|
|          10         |         34       |      01/02/2000     |         10       |
|---------------------|------------------|---------------------|------------------|
|          10         |         34       |      01/05/2000     |         11       |
|---------------------|------------------|---------------------|------------------|
|          10         |         40       |      01/05/2000     |         12       |
|---------------------|------------------|---------------------|------------------|
|          10         |         40       |      01/02/2000     |         13       |
|---------------------|------------------|---------------------|------------------|
|          10         |         40       |      01/02/2000     |         14       |
|---------------------|------------------|---------------------|------------------|
|          10         |         40       |      01/15/2000     |         15       |
|---------------------|------------------|---------------------|------------------|
|          10         |         34       |      01/16/2000     |         16       |
|---------------------|------------------|---------------------|------------------|
|          10         |         70       |      01/17/2000     |         17       |
|---------------------|------------------|---------------------|------------------|
|          10         |         70       |      01/28/2000     |         18       |
|---------------------|------------------|---------------------|------------------|

Мне нужно создать такое представление (получить количество дней, в течение которого файл остается в каждой единице)

|---------------------|------------------|---------------------|
|         FILE        |     UNIT         |    DAYS IN UNITY    |
|---------------------|------------------|---------------------|
|          10         |         34       |         3           |
|---------------------|------------------|---------------------|
|          10         |         40       |         10          |
|---------------------|------------------|---------------------|
|          10         |         34       |          1          |
|---------------------|------------------|---------------------|
|          10         |         70       |         11          |
|---------------------|------------------|---------------------|

Любой совет

Заранее спасибо

Ответы [ 2 ]

1 голос
/ 11 февраля 2020

Это форма пробелов и островков. Для этой цели я думаю о разнице номеров строк:

select file, unit, 
       (lead(min(date), 1, max(date)) over (partition by file, unit) -
        min(date)
       ) as days_in_unity
from (select t.*,
             row_number() over (partition by file order by id_sequence) as seqnum,
             row_number() over (partition by file, unit order by id_sequence) as seqnum_2
      from t
     ) t
group by file, unit, (seqnum - seqnum_2)

Почему эту работу немного сложно объяснить. Если вы посмотрите на результаты подзапроса, вы увидите, как разница двух номеров строк постоянна для строк с одинаковым значением unit.

0 голосов
/ 12 февраля 2020

Если вы используете более новую версию 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 здесь .

...