Oracle
 sql >> Datenbank >  >> RDS >> Oracle

Zusammenfügen aufeinanderfolgender Datumsgültigkeitsintervalle

Dies ist ein Lücken-und-Inseln-Problem. Es gibt verschiedene Möglichkeiten, sich ihm zu nähern; dabei wird lead verwendet und lag Analysefunktionen:

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-Geige

Die innerste Abfrage untersucht die vorangehenden und folgenden Datensätze für das Produkt und behält die Start- und/oder Endzeit nur bei, wenn die Datensätze nicht zusammenhängend sind:

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 

Die nächste Auswahlebene entfernt diejenigen, die in der Mitte des Zeitraums liegen, wo beide Daten durch die innere Abfrage ausgeblendet wurden, was ergibt:

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 

Die äußere Abfrage kollabiert dann diese benachbarten Paare; Ich habe den einfachen Weg gewählt, Duplikate zu erstellen und sie dann mit distinct zu eliminieren , aber Sie können es auch auf andere Weise tun, z. B. indem Sie beide Werte in eines der Zeilenpaare einfügen und beide Werte in der anderen null belassen und diese dann mit einer anderen Auswahlebene eliminieren, aber ich denke, dass distinct hier in Ordnung ist.

Wenn Ihr realer Anwendungsfall Zeiten und nicht nur Daten enthält, müssen Sie den Vergleich in der inneren Abfrage anpassen. statt +/- 1 vielleicht ein Intervall von 1 Sekunde oder 1/86400, wenn Sie es vorziehen, aber das hängt von der Genauigkeit Ihrer Werte ab.