sql >> Databasteknik >  >> RDS >> Oracle

Sammanfoga på varandra följande datum giltighetsintervall

Detta är ett problem med luckor och öar. Det finns olika sätt att närma sig det; detta använder lead och lag analytiska funktioner:

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

Den innersta frågan tittar på föregående och följande poster för produkten och behåller endast start- och/eller sluttid om posterna inte är sammanhängande:

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 

Nästa nivå av markering tar bort de som är i mitten av perioden, där båda datumen släcktes av den inre frågan, vilket ger:

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 

Den yttre frågan kollapsar sedan de intilliggande paren; Jag har använt den enkla vägen att skapa dubbletter och sedan eliminera dem med distinct , men du kan göra det på andra sätt, som att lägga båda värdena i ett av radparen och lämna båda värdena i den andra null, och sedan eliminera de med ett annat lager av markeringar, men jag tror att distinkt är OK här.

Om ditt verkliga användningsfall har tider, inte bara datum, måste du justera jämförelsen i den inre frågan; snarare än +/- 1, ett intervall på 1 sekund kanske, eller 1/86400 om du föredrar det, men beror på precisionen i dina värden.



  1. använder mysql_close()

  2. SQL-fönsterfunktion med en where-klausul?

  3. Vad är skillnaden mellan enkla citattecken och dubbla citattecken i PostgreSQL?

  4. PostgreSQL ändra typ tidsstämpel utan tidszon -> med tidszon