2014-07-14 20 views
5

Supponiamo di avere una tabella con un identificatore, un'ora di inizio e un'ora di fine. Questi orari di inizio e fine possono essere qualsiasi periodo di tempo. L'ora di inizio è sempre prima dell'ora di fine. Supponiamo che non ci siano valori nulli.Come trovare la maggior parte del periodo di tempo sovrapposto con intervalli di date

Che tipo di query potrebbe dirmi il momento più "popolare", vale a dire dove i due intervalli in ogni riga si sovrappongono con la maggior parte delle altre righe?

L'applicazione reale di questo è che si tratta di una tabella che registra gli accessi e le disconnessioni degli utenti. Voglio scrivere una query che mi dirà quando gli utenti più concorrenti sono stati registrati e vedere quale periodo di tempo è stato.

Grazie.

risposta

5

Ecco un esempio di soluzione utilizzando una semplice self-join e un GROUP BY:

WITH d(id, t1, t2) AS (
    SELECT 1, date '2010-01-01', date '2010-03-01' FROM DUAL UNION ALL 
    SELECT 2, date '2010-02-01', date '2010-04-01' FROM DUAL UNION ALL 
    SELECT 3, date '2010-02-01', date '2010-04-01' FROM DUAL UNION ALL 
    SELECT 4, date '2010-01-01', date '2010-01-03' FROM DUAL UNION ALL 
    SELECT 5, date '2011-01-01', date '2011-02-15' FROM DUAL 
) 
SELECT d1.id, d1.t1, d1.t2, 
     COUNT(*) "Overlap count", 
     LISTAGG('[' || d2.t1 || ', ' || d2.t2 || ']', ', ') 
     WITHIN GROUP (ORDER BY d2.id) "Overlapping intervals" 
FROM d d1 
LEFT OUTER JOIN d d2 
ON d2.t1 <= d1.t2 AND d1.t1 <= d2.t2 
GROUP BY d1.id, d1.t1, d1.t2 
ORDER BY COUNT(*) DESC 

Il "Overlapping intervals" aggregazione è solo per illustrazione.

SQLFiddle

... con uscita:

| ID | OVERLAP COUNT |                   OVERLAPPING INTERVALS | 
|----|---------------|------------------------------------------------------------------------------------------------| 
| 1 |    4 | [01-JAN-10, 01-MAR-10], [01-FEB-10, 01-APR-10], [01-FEB-10, 01-APR-10], [01-JAN-10, 03-JAN-10] | 
| 2 |    3 |       [01-JAN-10, 01-MAR-10], [01-FEB-10, 01-APR-10], [01-FEB-10, 01-APR-10] | 
| 3 |    3 |       [01-JAN-10, 01-MAR-10], [01-FEB-10, 01-APR-10], [01-FEB-10, 01-APR-10] | 
| 4 |    2 |             [01-JAN-10, 01-MAR-10], [01-JAN-10, 03-JAN-10] | 
| 5 |    1 |                   [01-JAN-11, 15-FEB-11] | 
6

Ci sono diversi approcci a questo. Uno utilizza sottoquery correlate. Non è molto divertente. Invece, usiamo il metodo della somma cumulativa perché hai Oracle.

La chiave è iniziare con un elenco di timestamp con un valore di +1 per un inizio e -1 per un fine. Questo è facile:

select t.* 
from ((select starttime as thetime, 1 as value from table t) union all 
     (select endtime, -1 as value from table t) 
    ) t 

Ora, la somma cumulativa del value indica il numero di sovrapposizioni attivi in ​​un dato momento:

select t.*, sum(value) over (order by thetime) as numactives 
from ((select starttime as thetime, 1 as value from table t) union all 
     (select endtime, -1 as value from table t) 
    ) t 

questo risolve il problema. Probabilmente vorrai aggiungere un order by numactives desc per orari specifici.

Problemi correlati