2010-11-01 23 views
12

Ho due tabelle in SQL e devo essere in grado di fare un join basato sul timestamp nella tabella B che è precedente o uguale al timestamp nella tabella A.Query SQL per unire due tabelle in base al timestamp più ravvicinato

Quindi, ecco alcuni dati falsi per due tavoli e l'output desiderato:

Casi

chiusi (Tabella 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 | 

Classificazione (tabella 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 | 

risultati desiderati

 
| 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 | 

Quindi, so che il codice deve essere simile al seguente, non riesco proprio a capire cosa fare con il ON porzione del JOIN ($ 1 e $ 2 sono le variabili che saranno passate a):

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; 

so che potrei usare un sub-select, ma questo sarà operativo su a Le poche migliaia di file, probabilmente di più, e ho bisogno che sia molto veloce; quindi speravo in una semplice clausola che potesse farlo.

+0

Penso che è necessario il sub-select. Hai testato la performance e l'hai trovato inaccettabile? – Beth

+0

se la versione di SQL che stai utilizzando supporta funzioni di analisi delle finestre, dovresti essere in grado di farlo senza una sottoselezione, ma alcune versioni di SQL non le supportano. Per una singola sotto-selezione su poche migliaia di righe, le prestazioni non dovrebbero essere troppo brutte. (La sotto-selezione sarà sulla tabella di classificazione - questo avrà davvero più di qualche migliaio di righe?) –

+0

@Mark - In realtà, a pensarci bene, la tabella di classificazione dovrebbe avere un numero di file inferiore a quello di I'll controlla se i dati sono effettivamente cambiati rispetto alla versione più recente.Quindi suppongo che la sotto-selezione avrebbe funzionato bene, ma penso che aggiungere l'ora di fine sia una soluzione molto più pulita. –

risposta

7

Se è possibile apportare modifiche alle strutture della tabella, è consigliabile modificare la tabella di classificazione per includere una data di fine e una data di inizio: sarà molto più facile unirsi alla tabella in questo modo.

In caso contrario, suggerisco il seguente:

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 - con la data di fine sulla classificazione:

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

Puoi spiegare brevemente come sarebbe utile modificare la struttura della tabella per includere la data di fine? Potrei teoricamente farlo impostando sempre la data di fine attuale in un tempo molto lontano nel futuro e aggiornando la data di fine della voce precedente come data di inizio delle voci correnti. –

+0

@Topher - aggiunta query aggiuntiva; non è richiesta alcuna sotto-selezione e la query deve essere sargabile. –

+0

+ 1/Accettato - Grazie per l'aiuto Marco! Aggiungerò solo l'ora di fine per semplificarti la vita. –

0

modificare il timestamp e utilizzare un int come chiave per connettere le tabelle. questo funzionerà molto più velocemente quindi confrontando data

tabella 1 campo1 campo2 campo3 ConnectorField

table2 campo1 campo2 campo3 ConnectorField

e tutto quello che dovete fare è select * from table1 T1 inner join table2 T2 su T1.ConnectorField = T2.ConnectorField

+0

Questa soluzione richiede che l'OP possa modificare la struttura del database esistente e impedire le modifiche agli intervalli di date delle classificazioni rispetto ai casi esistenti. –

+0

Non importa se utilizzo un numero intero (non sono mai memorizzate le marche temporali come numeri interi), questo comunque non risolve il problema di "più vicino a". Avrei ancora bisogno di fare il join basato su esattamente un intero nella tabella B che è minore o uguale al numero intero nella tabella A. –

+0

@Topher, probabilmente non ho capito le specifiche. – 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

Ciò restituirà tutte le classificazioni dopo il timestamp del caso per ciascun caso, anziché solo la classificazione applicabile - quindi per l'esempio fornito, si vedrebbero 11 righe restituite invece del 4 richiesto. –

+0

@ Mann Mannister - Esattamente. Ho bisogno solo delle 4 righe (ho intenzione di fare un conteggio e raggrupparle in seguito). –