sql >> Base de Datos >  >> RDS >> Oracle

Unión de intervalos de validez de fechas consecutivas

Este es un problema de lagunas e islas. Hay varias formas de abordarlo; esto usa lead y lag funciones analíticas:

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 consulta más interna examina los registros anteriores y posteriores del producto y solo conserva la hora de inicio y/o finalización si los registros no son contiguos:

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 

El siguiente nivel de selección elimina los que están en la mitad del período, donde ambas fechas fueron borradas por la consulta interna, lo que da:

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 consulta externa colapsa esos pares adyacentes; He usado la ruta fácil de crear duplicados y luego eliminarlos con distinct , pero puede hacerlo de otras maneras, como poner ambos valores en uno de los pares de filas y dejar ambos valores en el otro nulo, y luego eliminar aquellos con otra capa de selección, pero creo que distinguir está bien aquí.

Si su caso de uso del mundo real tiene tiempos, no solo fechas, entonces deberá ajustar la comparación en la consulta interna; en lugar de +/- 1, un intervalo de 1 segundo quizás, o 1/86400 si lo prefiere, pero depende de la precisión de sus valores.