Oracle
 sql >> Database >  >> RDS >> Oracle

Unendo insieme intervalli di validità di data consecutivi

Questo è un problema di lacune e isole. Ci sono vari modi per affrontarlo; questo utilizza lead e lag funzioni analitiche:

select distinct product,
  case when start_date is null then lag(start_date)
    over (partition by product order by rn) else start_date end as start_date,
  case when end_date is null then lead(end_date)
    over (partition by product order by rn) else end_date end as end_date
from (
  select product, start_date, end_date, rn
  from (
    select t.product,
      case when lag(end_date)
          over (partition by product order by start_date) is null
        or lag(end_date)
          over (partition by product order by start_date) != start_date - 1
        then start_date end as start_date,
      case when lead(start_date)
          over (partition by product order by start_date) is null
        or lead(start_date)
          over (partition by product order by start_date) != end_date + 1
        then end_date end as end_date,
      row_number() over (partition by product order by start_date) as rn
    from t
  )
  where start_date is not null or end_date is not null
)
order by start_date, product;

PRODUCT START_DATE END_DATE
------- ---------- ---------
A       01-JUL-13  30-SEP-13 
B       01-OCT-13  30-NOV-13 
A       01-DEC-13  31-MAR-14 

SQL Fiddle

La query più interna esamina i record precedenti e successivi per il prodotto e conserva l'ora di inizio e/o di fine solo se i record non sono contigui:

select t.product,
  case when lag(end_date)
      over (partition by product order by start_date) is null
    or lag(end_date)
      over (partition by product order by start_date) != start_date - 1
    then start_date end as start_date,
  case when lead(start_date)
      over (partition by product order by start_date) is null
    or lead(start_date)
      over (partition by product order by start_date) != end_date + 1
    then end_date end as end_date
from t;

PRODUCT START_DATE END_DATE
------- ---------- ---------
A       01-JUL-13            
A                            
A                  30-SEP-13 
A       01-DEC-13            
A                            
A                            
A                  31-MAR-14 
B       01-OCT-13            
B                  30-NOV-13 

Il livello successivo di selezione rimuove quelle che sono a metà periodo, in cui entrambe le date sono state cancellate dalla query interna, che fornisce:

PRODUCT START_DATE END_DATE
------- ---------- ---------
A       01-JUL-13            
A                  30-SEP-13 
A       01-DEC-13            
A                  31-MAR-14 
B       01-OCT-13            
B                  30-NOV-13 

La query esterna quindi comprime quelle coppie adiacenti; Ho utilizzato il percorso più semplice per creare duplicati e quindi eliminarli con distinct , ma puoi farlo in altri modi, come inserire entrambi i valori in una delle coppie di righe e lasciare entrambi i valori nell'altro null, quindi eliminare quelli con un altro livello di selezione, ma penso che distinto sia OK qui.

Se il tuo caso d'uso nel mondo reale ha orari, non solo date, dovrai regolare il confronto nella query interna; anziché +/- 1, forse un intervallo di 1 secondo, o 1/86400 se preferisci, ma dipende dalla precisione dei tuoi valori.