Oracle
 sql >> Base de données >  >> RDS >> Oracle

Regroupement d'intervalles de validité de dates consécutives

Il s'agit d'un problème de lacunes et d'îlots. Il existe différentes façons de l'aborder; cela utilise lead et lag fonctions analytiques :

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 requête la plus interne examine les enregistrements précédents et suivants du produit et ne retient l'heure de début et/ou de fin que si les enregistrements ne sont pas contigus :

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 

Le niveau suivant de sélection supprime ceux qui sont au milieu de la période, où les deux dates ont été masquées par la requête interne, ce qui donne :

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 requête externe réduit ensuite ces paires adjacentes ; J'ai utilisé la voie simple consistant à créer des doublons puis à les éliminer avec distinct , mais vous pouvez le faire d'une autre manière, comme mettre les deux valeurs dans l'une des paires de lignes et laisser les deux valeurs dans l'autre null, puis éliminer celles avec une autre couche de sélection, mais je pense que distinct est OK ici.

Si votre cas d'utilisation réel a des heures, pas seulement des dates, vous devrez alors ajuster la comparaison dans la requête interne; plutôt que +/- 1, un intervalle de 1 seconde peut-être, ou 1/86400 si vous préférez, mais dépend de la précision de vos valeurs.