2015-08-01 15 views
7

Ho una tabella in un database Oracle. Lo schema èjoin (self) a intervalli di tempo

create table PERIODS 
( 
    ID NUMBER, 
    STARTTIME TIMESTAMP, 
    ENDTIME TIMESTAMP, 
    TYPE VARCHAR2(100) 
) 

ho due differenti TYPE's: TYPEA e TYPEB. Hanno tempi di inizio e di fine indipendenti e possono sovrapporsi. Quello che vorrei trovare sono i periodi di TYPEB avviati, completamente contenuti o terminati entro un determinato periodo di TYPEA.

Ecco quello che mi è venuto in mente finora (con alcuni dati di esempio)

WITH mydata 
    AS (SELECT 100             ID, 
       To_timestamp('2015-08-01 11:00', 'YYYY-MM-DD HH24:MI') STARTTIME, 
       To_timestamp('2015-08-01 11:20', 'YYYY-MM-DD HH24:MI') ENDTIME, 
       'TYPEA'            TYPE 
     FROM dual 
     UNION ALL 
     SELECT 110             ID, 
       To_timestamp('2015-08-01 11:30', 'YYYY-MM-DD HH24:MI') STARTTIME, 
       To_timestamp('2015-08-01 11:50', 'YYYY-MM-DD HH24:MI') ENDTIME, 
       'TYPEA'            TYPE 
     FROM dual 
     UNION ALL 
     SELECT 120             ID, 
       To_timestamp('2015-08-01 12:00', 'YYYY-MM-DD HH24:MI') STARTTIME, 
       To_timestamp('2015-08-01 12:20', 'YYYY-MM-DD HH24:MI') ENDTIME, 
       'TYPEA'            TYPE 
     FROM dual 
     UNION ALL 
     SELECT 105             ID, 
       To_timestamp('2015-08-01 10:55', 'YYYY-MM-DD HH24:MI') STARTTIME, 
       To_timestamp('2015-08-01 11:05', 'YYYY-MM-DD HH24:MI') ENDTIME, 
       'TYPEB'            TYPE 
     FROM dual 
     UNION ALL 
     SELECT 108             ID, 
       To_timestamp('2015-08-01 11:05', 'YYYY-MM-DD HH24:MI') STARTTIME, 
       To_timestamp('2015-08-01 11:15', 'YYYY-MM-DD HH24:MI') ENDTIME, 
       'TYPEB'            TYPE 
     FROM dual 
     UNION ALL 
     SELECT 111             ID, 
       To_timestamp('2015-08-01 11:15', 'YYYY-MM-DD HH24:MI') STARTTIME, 
       To_timestamp('2015-08-01 12:25', 'YYYY-MM-DD HH24:MI') ENDTIME, 
       'TYPEB'            TYPE 
     FROM dual), 
    typeas 
    AS (SELECT starttime, 
       endtime 
     FROM mydata 
     WHERE TYPE = 'TYPEA'), 
    typebs 
    AS (SELECT id, 
       starttime, 
       endtime 
     FROM mydata 
     WHERE TYPE = 'TYPEB') 
SELECT id 
FROM typebs b 
     join typeas a 
     ON (b.starttime BETWEEN a.starttime AND a.endtime) 
      OR (b.starttime BETWEEN a.starttime AND a.endtime 
        AND b.endtime BETWEEN a.starttime AND a.endtime) 
      OR (b.endtime BETWEEN a.starttime AND a.endtime) 
ORDER BY id; 

Questo sembra funzionare in linea di principio, il risultato della query di cui sopra è

 ID 
---------- 
     105 
     108 
     111 

quindi seleziona i tre periodi TYPEB avviati o terminati all'interno del primo periodo TYPEA.

Il problema è che la tabella ha circa 200.000 voci e già a questa dimensione la query precedente è piuttosto lenta --- il che è molto sorprendente per me dato che il numero di entrambe le voci TYPEA e TYPEB è piuttosto basso (1-2k)

Esiste un modo più efficiente per eseguire questo tipo di self join? Ho perso qualcos'altro nella mia domanda?

+0

La tabella ha 200.000 righe, ci sono solo due diversi valori di 'tipo', ma ogni' tipo' ha solo 1.000-2.000 righe. Questo implica che ~ 196.000 righe nella tabella abbiano un 'NULL' per il loro' type'? Se stai chiedendo una domanda di ottimizzazione, puoi pubblicare il piano di query che stai ricevendo e quali indici sono disponibili? –

+0

La tabella ha 200k righe, la maggior parte delle quali ha un tipo che non è rilevante per me per questa query. – Erik

+0

@JustinCave potrebbe essere una domanda di ottimizzazione, ma forse sono semplicemente ignaro di qualche altra caratteristica che renderebbe questa query molto più veloce senza messa a punto. – Erik

risposta

1

Forse la pena di provare (anche è necessario scrivere le condizioni più restrittivi, alla fine, in Oracle, non chiedetemi perché o credetemi, meglio fare il proprio test di performance):

SELECT 
    p.id 
FROM 
    periods p 
WHERE 
    EXISTS(SELECT * FROM periods q WHERE 
     (p.startTime BETWEEN q.startTime AND q.endTime 
     OR p.endTime BETWEEN q.startTime AND q.endTime 
     OR p.startTime < q.startTime AND p.endTime > q.endTime -- overlapping correction, remove if not needed 
    ) AND q.type = 'TYPEA' 
    ) AND p.type = 'TYPEB' 
ORDER BY 
    p.id 
; 
+0

E q.type = 'TYPEA' dovrebbe essere all'interno della subquery – Bulat

+1

È all'interno, forse le parentesi sembrano strane, ma sottolineo di più sui rientri. – maraca

Problemi correlati