2009-06-02 5 views
5
site_id | start_date | end_date 
     1 | oct 1, 08 | oct 2, 08 
     1 | oct 2, 08 | oct 3, 08 
... 
     1 | oct 30, 08 | oct 31, 08 
     2 | oct 1, 08 | oct 2, 08 
     2 | oct 2, 08 | oct 3, 08 
... 
     2 | oct 30, 08 | oct 31, 08 

J'ai une table qui contient 1 enregistrement par site par jour du mois (par mois de l'année). Je dois être en mesure de déterminer si un site pour un mois donné a au moins 15 enregistrements contigus, et j'ai besoin de connaître la date de début et de fin de cette série de jours contigus. Je peux le faire dans une procédure stockée, mais j'espérais que cela pourrait être accompli dans une seule requête. Je traite un ensemble de données assez important, au moins 30 millions d'enregistrements par mois.Comment puis-je rechercher efficacement des ensembles contigus de dates dans mon ensemble de données?

Exemple: Résultats

site_id | contiguous_start_date | contiguous_end_date 
     1 | oct 5, 2008   | oct 20, 2008 
     2 | oct 10    | oct 30, 2008 
     3 | oct 1     | oct 31, 2008 

Merci pour votre aide!

+0

Quand 'end_date' n'est-il pas égal à 'start_date + 1 day'? Parce que la requête est plus simple s'il n'y a pas besoin de regarder les deux colonnes. –

Répondre

4

Voici un exemple de la façon de faire une telle requête:

SQL> create table t (site_id,start_date,end_date) 
    2 as 
    3 select 1, date '2008-10-01', date '2008-10-02' from dual union all 
    4 select 1, date '2008-10-02', date '2008-10-03' from dual union all 
    5 select 1, date '2008-10-03', date '2008-10-30' from dual union all 
    6 select 1, date '2008-10-30', date '2008-10-31' from dual union all 
    7 select 2, date '2008-10-01', date '2008-10-02' from dual union all 
    8 select 2, date '2008-10-02', date '2008-10-03' from dual union all 
    9 select 2, date '2008-10-03', date '2008-10-04' from dual union all 
10 select 2, date '2008-10-04', date '2008-10-05' from dual union all 
11 select 2, date '2008-10-05', date '2008-10-06' from dual union all 
12 select 2, date '2008-10-06', date '2008-10-07' from dual union all 
13 select 2, date '2008-10-07', date '2008-10-08' from dual union all 
14 select 2, date '2008-10-08', date '2008-10-09' from dual union all 
15 select 2, date '2008-10-09', date '2008-10-10' from dual union all 
16 select 2, date '2008-10-10', date '2008-10-11' from dual union all 
17 select 2, date '2008-10-11', date '2008-10-12' from dual union all 
18 select 2, date '2008-10-12', date '2008-10-13' from dual union all 
19 select 2, date '2008-10-13', date '2008-10-14' from dual union all 
20 select 2, date '2008-10-14', date '2008-10-15' from dual union all 
21 select 2, date '2008-10-15', date '2008-10-16' from dual union all 
22 select 2, date '2008-10-16', date '2008-10-17' from dual union all 
23 select 2, date '2008-10-17', date '2008-10-18' from dual union all 
24 select 2, date '2008-10-18', date '2008-10-19' from dual union all 
25 select 2, date '2008-10-19', date '2008-10-20' from dual union all 
26 select 3, date '2008-10-01', date '2008-10-02' from dual union all 
27 select 3, date '2008-10-02', date '2008-10-03' from dual union all 
28 select 3, date '2008-10-03', date '2008-10-04' from dual union all 
29 select 3, date '2008-10-04', date '2008-10-05' from dual union all 
30 select 3, date '2008-10-05', date '2008-10-06' from dual union all 
31 select 3, date '2008-10-06', date '2008-10-07' from dual union all 
32 select 3, date '2008-10-07', date '2008-10-08' from dual union all 
33 select 3, date '2008-10-08', date '2008-10-09' from dual union all 
34 select 3, date '2008-10-09', date '2008-10-10' from dual union all 
35 select 3, date '2008-10-30', date '2008-10-31' from dual 
36/

Tabel is aangemaakt. 

Et puis la requête:

SQL> select site_id 
    2  , min(start_date) contiguous_start_date 
    3  , max(end_date) contiguous_end_date 
    4  , count(*) number_of_contiguous_records 
    5 from (select site_id 
    6    , start_date 
    7    , end_date 
    8    , max(rn) over (partition by site_id order by start_date) maxrn 
    9    from (select site_id 
10       , start_date 
11       , end_date 
12       , case lag(end_date) over (partition by site_id order by start_date) 
13        when start_date then null 
14        else rownum 
15       end rn 
16      from t 
17     ) 
18   ) 
19 group by site_id 
20  , maxrn 
21 order by site_id 
22  , contiguous_start_date 
23/

Et les résultats:

SITE_ID CONTIGUOUS_START_DA CONTIGUOUS_END_DATE NUMBER_OF_CONTIGUOUS_RECORDS 
---------- ------------------- ------------------- ---------------------------- 
     1 01-10-2008 00:00:00 31-10-2008 00:00:00       4 
     2 01-10-2008 00:00:00 20-10-2008 00:00:00       19 
     3 01-10-2008 00:00:00 10-10-2008 00:00:00       9 
     3 30-10-2008 00:00:00 31-10-2008 00:00:00       1 

4 rijen zijn geselecteerd. 

Cordialement, Rob .

+0

Approche intelligente pour résoudre le problème. Merci. –

+0

+1 Cool, lag (end_date) = date_début. – Andomar

1

Ceci est certainement très possible. J'ai résolu un problème similaire dans SQL Server il y a quelques mois. Je ne sais rien de la syntaxe Oracle, donc je crains de ne pas pouvoir convertir si pour vous, mais si vous êtes solide avec Oracle, this devrait être suffisant pour vous y rendre.

-1

Votre structure de base de données ne convient pas à la logique métier que vous avez:

  • date_fin est toujours le lendemain start_date alors pourquoi vous devez stocker dans le db?
  • Je vois que dans l'exemple de données que vous avez donné il n'y a pas d'espaces dans la plage de dates pour un seul site. Cela signifie que vous n'avez pas besoin de stocker toutes les dates juste commencer et arrêter la date.

30 millions de disques par mois est vraiment table pour la requête que vous devez écrire. Faire un refactoring structurel de ce tableau est mon conseil.

Questions connexes