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

Sammenføjning af på hinanden følgende dato gyldighedsintervaller

Dette er et hul-og-ø-problem. Der er forskellige måder at gribe det an på; dette bruger lead og lag analytiske 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 inderste forespørgsel ser på de foregående og følgende poster for produktet og beholder kun start- og/eller sluttidspunktet, hvis posterne ikke er sammenhængende:

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 

Det næste niveau af valg fjerner dem, der er midt i perioden, hvor begge datoer blev blanket af den indre forespørgsel, hvilket giver:

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 ydre forespørgsel kollapser derefter de tilstødende par; Jeg har brugt den nemme måde at oprette dubletter og derefter fjerne dem med distinct , men du kan gøre det på andre måder, som at sætte begge værdier ind i et af rækkeparrene og lade begge værdier være i den anden null, og så eliminere dem med et andet lag af markeringer, men jeg synes, at distinct er OK her.

Hvis din brugssag i den virkelige verden har tidspunkter, ikke kun datoer, så bliver du nødt til at justere sammenligningen i den indre forespørgsel; snarere end +/- 1, et interval på 1 sekund måske, eller 1/86400, hvis du foretrækker det, men afhænger af præcisionen af ​​dine værdier.



  1. Mange-til-mange forhold vælg og bestil efter

  2. Oracle hvordan man eksporterer forespørgsel til en tekst/csv-fil

  3. Sådan gemmer du resultatet af MySql-forespørgsel i variabel ved hjælp af node-mysql

  4. Hvad gør du i SQL Server for at OPRETTE ELLER ÆNDRE?