2010-11-01 13 views
12

Mam dwie tabele w SQL i muszę być w stanie wykonać sprzężenie w oparciu o znacznik czasu w tabeli B, który jest wcześniejszy lub równy znacznikowi czasowemu w tabeli A.Zapytanie SQL do połączenia dwóch tabel na podstawie najbliżej datownika

Więc, oto niektóre fałszywe dane dla dwóch tabel i sygnał wyjściowy:

zamkniętych spraw (tabela a)

 
| id | resolution |   timestamp   | 
------------------------------------------------ 
| 1 |  solved | 2006-10-05 11:55:44.888153 | 
| 2 |  closed | 2007-10-07 12:34:17.033498 | 
| 3 | trashed | 2008-10-09 08:19:36.983747 | 
| 4 |  solved | 2010-10-13 04:28:14.348753 | 

Klasyfikacja (tabela B)

 

| id | value |   timestamp   | 
------------------------------------------------- 
| 1 | freshman | 2006-01-01 12:02:44.888153 | 
| 2 | sophomore | 2007-01-01 12:01:19.984333 | 
| 3 |  junior | 2008-01-01 12:02:28.746149 | 

Pożądane Wyniki

 
| id | resolution |   timestamp   | value | 
-------------------------------------------------------------- 
| 1 |  solved | 2006-10-05 11:55:44.888153 | freshman | 
| 2 |  closed | 2007-10-07 12:34:17.033498 | sophomore | 
| 3 | trashed | 2008-10-09 08:19:36.983747 |  junior | 
| 4 |  solved | 2010-10-13 04:28:14.348753 |  junior | 

Tak, wiem, że kod musi wyglądać jak poniżej, po prostu nie może dowiedzieć się, co zrobić z częścią ON z JOIN ($ 1 i $ 2 to zmienne, które zostaną przekazane w):

SELECT case.id, case.resolution, case.timestamp, class.value 
    FROM closed_cases AS case 
    LEFT JOIN classifications AS class ON ??? 
    WHERE case.timestamp BETWEEN $1 AND $2; 

wiem, że mógłbym użyć sub-select, ale to będzie działać na w le kilka tysięcy wierszy, prawdopodobnie więcej i potrzebuję, aby było naprawdę szybko; więc liczyłem na prostą klauzulę, która mogłaby to zrobić.

+0

myślę musisz sub-select. Czy przetestowałeś wydajność i okazało się, że jest nie do przyjęcia? – Beth

+0

jeśli wersja SQL, z której korzystasz obsługuje funkcje analityczne okienkowania, powinieneś być w stanie to zrobić bez sub-select, ale niektóre wersje SQL nie obsługują ich. W przypadku pojedynczego sub-select w kilku tysiącach wierszy wydajność nie powinna być zbyt zła. (Sub-select znajdzie się w tabeli klasyfikacji - czy będzie to naprawdę więcej niż kilka tysięcy wierszy?) –

+0

@Mark - Właściwie, pomyśl o tym, tabela klasyfikacji powinna mieć mniej wierszy niż to, co ja. sprawdzać, czy dane rzeczywiście zmieniły się z najbardziej aktualnej wersji.Sądzę więc, że sub-select działałby dobrze, ale myślę, że dodanie czasu końca jest zdecydowanie czystszym rozwiązaniem. –

Odpowiedz

7

Jeśli możesz wprowadzać zmiany w strukturach tabel, zalecam zmianę tabeli klasyfikacji tak, aby zawierała datę końcową, a także datę rozpoczęcia - znacznie łatwiej będzie przyłączyć się do tabeli w ten sposób.

Jeśli nie, proponuję następujące:

SELECT case.id, case.resolution, case.timestamp, class.value 
    FROM closed_cases AS case 
    LEFT JOIN (select c.*, 
        (select min(timestamp) 
        from classifications c1 
         where c1.timestamp > c.timestamp) timeend 
      from classifications c) AS class 
    ON case.timestamp >= class.timestamp and 
    (case.timestamp < class.timeend or class.timeend IS NULL) 
    WHERE case.timestamp BETWEEN $1 AND $2; 

EDIT - z datą końcową dotyczącą klasyfikacji:

SELECT case.id, case.resolution, case.timestamp, class.value 
    FROM closed_cases AS case 
    LEFT JOIN classifications AS class 
    ON case.timestamp >= class.timestamp and case.timestamp < class.timeend 
    WHERE case.timestamp BETWEEN $1 AND $2; 
+0

Czy możesz krótko wyjaśnić, w jaki sposób pomocna byłaby zmiana struktury tabeli na datę końcową? Teoretycznie mogłem to zrobić, zawsze ustawiając aktualną datę końcową na jakiś czas daleko w przyszłości i aktualizując datę końcową poprzedniego wpisu na datę rozpoczęcia bieżących wpisów. –

+0

@Topher - dodano dodatkowe zapytanie; nie jest wymagana żadna sub-select, a zapytanie powinno być sargable. –

+0

+ 1/Zaakceptowany - dzięki za pomoc Mark! Po prostu dodam czas zakończenia, aby ułatwić życie. –

0

zmienić sygnaturę czasową i użyć int jako klucza do łączenia tabel. to będzie działać znacznie szybciej następnie porównując datę

Tabela 1 pole1 Pole2 field3 ConnectorField

Tabela 2 pole1 pole2 field3 ConnectorField

i wszystko, co trzeba zrobić, to select * from table1 T1 internal join table2 T2 na T1.ConnectorField = T2.ConnectorField

+0

To rozwiązanie wymaga, aby OP mógł zmienić strukturę istniejącej bazy danych i zapobiegać zmianom zakresu dat klasyfikacji w odniesieniu do istniejących przypadków. –

+0

Nie ma znaczenia, czy używam liczby całkowitej (czy nie są znacznikami czasu przechowywanymi jako liczby całkowite), to nadal nie rozwiązuje problemu "najbliższego". Wciąż musiałbym wykonać sprzężenie w oparciu o dokładnie jedną liczbę całkowitą w tabeli B, która jest mniejsza lub równa liczbie całkowitej w tabeli A. –

+0

@Topher, prawdopodobnie nie zrozumiałem specyfikacji. – none

-1
SELECT case.id, case.resolution, case.timestamp, class.value 
    FROM closed_cases AS case 
    LEFT JOIN classifications AS class 
    ON case.timestamp >= class.timestamp 
    WHERE case.timestamp BETWEEN $1 AND $2; 
+0

Spowoduje to zwrócenie wszystkich klasyfikacji po znaczniku czasu dla każdego przypadku, a nie tylko odpowiedniej klasyfikacji - tak więc w podanym przykładzie zobaczysz 11 zwróconych wierszy zamiast 4 wymaganych. –

+0

@ Mark Mannister - Dokładnie. Potrzebuję tylko 4 wierszy (mam zamiar zrobić liczenie i pogrupować je później). –

Powiązane problemy