sql >> Database >  >> RDS >> Oracle

Opeenvolgende geldigheidsintervallen van de datum samenvoegen

Dit is een gaps-and-islands-probleem. Er zijn verschillende manieren om het te benaderen; dit gebruikt lead en lag analytische functies:

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

De binnenste query kijkt naar de voorgaande en volgende records voor het product en behoudt alleen de begin- en/of eindtijd als de records niet aaneengesloten zijn:

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 

Het volgende selectieniveau verwijdert de datums die halverwege de periode zijn, waarbij beide datums werden weggelaten door de innerlijke query, wat het volgende oplevert:

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 

De buitenste query vouwt vervolgens die aangrenzende paren samen; Ik heb de gemakkelijke manier gebruikt om duplicaten te maken en ze vervolgens te verwijderen met distinct , maar je kunt het op andere manieren doen, zoals beide waarden in een van de paren rijen plaatsen en beide waarden in de andere null laten, en die vervolgens elimineren met een andere laag van select, maar ik denk dat onderscheiden hier OK is.

Als uw praktijkvoorbeeld tijden heeft, niet alleen datums, dan moet u de vergelijking in de inner query aanpassen; in plaats van +/- 1, een interval van misschien 1 seconde, of 1/86400 als je wilt, maar hangt af van de nauwkeurigheid van je waarden.



  1. wat verhindert dat PHP verbinding maakt met mijn MySQL-database?

  2. Hoe meerdere herhaalbare velden als array in de database op te slaan?

  3. Mijn SQL-joins gebruiken

  4. Krijg een lijst met MySQL-tabellen en voer een code uit voor elke tabel