2015-08-01 12 views
7

Mam tabelę w bazie danych Oracle. Schemat jest(self) łączyć w odstępach czasowych

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

Mam dwa różne TYPE's: TYPEA i TYPEB. Mają niezależne czasy rozpoczęcia i zakończenia i mogą się nakładać. Co chciałbym znaleźć, to okresy TYPEB, które rozpoczęły się, są całkowicie zamknięte lub zakończone w danym okresie TYPEA.

Oto co wymyśliłem tej pory (z kilkoma przykładowymi danymi)

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; 

To wydaje się działać na zasadzie, wynik z zapytania powyżej

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

więc wybiera trzy okresy TYPEB, które rozpoczęły się lub zakończyły w ciągu pierwszego okresu TYPEA.

Problem polega na tym, że tabela ma około 200 000 wpisów i już w tym rozmiarze powyższe zapytanie jest dość powolne --- co jest dla mnie bardzo zaskakujące, ponieważ liczba wpisów TYPEA i jest dość niska (1-2 k)

Czy istnieje skuteczniejszy sposób wykonywania tego typu łączenia? Czy w moim zapytaniu brakuje mi czegoś innego?

+0

tabela ma 200 000 wierszy, istnieją tylko dwie różne wartości typu "typ", ale każdy "typ" ma tylko 1000-2000 wierszy. Czy to oznacza, że ​​~ 196,000 wierszy w tabeli ma 'NULL' dla ich' type'? Jeśli zadasz pytanie dotyczące strojenia, możesz opublikować plan zapytania, który otrzymujesz i jakie indeksy są dostępne? –

+0

Tabela ma 200 tys. Wierszy, z których większość ma typ, który nie ma dla mnie znaczenia dla tego zapytania. – Erik

+0

@JustinCave to może być pytanie strojenia, ale być może jestem po prostu nieświadomy jakiejś innej funkcji, która uczyniłaby to zapytanie znacznie szybciej bez dostrajania. – Erik

Odpowiedz

1

Może warto spróbować (również trzeba napisać najbardziej ograniczających warunków w końcu w Oracle, nie pytaj mnie dlaczego i wierz mi, lepiej robić własne testy wydajności):

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

I q.type = 'TYPEA' powinno być wewnątrz podkwerendy – Bulat

+1

Jest w środku, może szelki wyglądają dziwnie, ale kładę większy nacisk na wcięcia. – maraca