2013-06-26 22 views
35

Ho una tabella dei dati del sensore. Ogni riga ha un ID sensore, un timestamp e altri campi. Voglio selezionare una singola riga con l'ultimo timestamp per ciascun sensore, inclusi alcuni degli altri campi.Come posso selezionare le righe con il timestamp più recente per ogni valore chiave?

ho pensato che la soluzione potrebbe essere quella di gruppo dal sensore id e poi ordinare da Max (timestamp) in questo modo:

SELECT sensorID,timestamp,sensorField1,sensorField2 
FROM sensorTable 
GROUP BY sensorID 
ORDER BY max(timestamp); 

Questo mi dà un errore che dice che "sensorField1 deve apparire nella clausola group by o essere utilizzato in un aggregato. "

Qual è il modo corretto di affrontare questo problema?

+0

Che motore DB stai utilizzando? –

+0

Mentre le risposte seguenti usando JOINs sul valore Max (timestamp) dovrebbero funzionare, suggerirei di unirmi a SensorReadingId se ne hai una sul SensorTable. –

risposta

12

È possibile selezionare solo le colonne presenti nel gruppo o utilizzate in una funzione di aggregazione. È possibile utilizzare un join per ottenere questo lavoro

select s1.* 
from sensorTable s1 
inner join 
(
    SELECT sensorID, max(timestamp) as mts 
    FROM sensorTable 
    GROUP BY sensorID 
) s2 on s2.sensorID = s1.sensorID and s1.timestamp = s2.mts 
+0

... o 'selezionare * da sensorTable dove (sensorID, timestamp) in (selezionare sensorID, max (timestamp) dal gruppo SensorTable per sensorID)'. – Arjan

+0

Penso che si applichi anche "LEFT JOIN", non solo "INNER JOIN"; e una parte "e s1.timestamp = s2.mts" non è IMHO nessario. Eppure, consiglio di creare un indice su due campi: sensorID + timestamp - la velocità delle query aumenta notevolmente! – Igor

3
WITH SensorTimes As (
    SELECT sensorID, MAX(timestamp) "LastReading" 
    FROM sensorTable 
    GROUP BY sensorID 
) 
SELECT s.sensorID,s.timestamp,s.sensorField1,s.sensorField2 
FROM sensorTable s 
INNER JOIN SensorTimes t on s.sensorID = t.sensorID and s.timestamp = t.LastReading 
+0

Funzionerà solo con MSSQL, giusto? –

+0

@juergend E oracle, postgresql, DB2 e alcuni altri. Questo fa parte dello standard sql99. –

13

Puoi partecipare al tavolo con se stesso (il sensore id), e aggiungere left.timestamp < right.timestamp come condizione di join. Quindi scegli le righe, dove right.id è null. Voilà, hai l'ultima voce per sensore.

http://sqlfiddle.com/#!9/45147/37

SELECT L.* FROM sensorTable L 
LEFT JOIN sensorTable R ON 
L.sensorID = R.sensorID AND 
L.timestamp < R.timestamp 
WHERE isnull (R.sensorID) 

Ma si prega di notare, che questo sarà molto alta intensità di risorse, se si dispone di una piccola quantità di ID e molti valori! Quindi, non lo consiglierei per qualche tipo di materiale di misurazione, in cui ogni sensore raccoglie un valore ogni minuto. Tuttavia in un caso d'uso, dove è necessario tenere traccia delle "Revisioni" di qualcosa che cambia solo "a volte", è facile.

+2

+1 per una soluzione insolita (volevo postare lo stesso, però) :) – fancyPants

+0

Grazie, mi hai appena salvato la vita :) – yossico

+0

@yossico sei il benvenuto. – dognose

30

Per ragioni di completezza, ecco un'altra soluzione possibile:

SELECT sensorID,timestamp,sensorField1,sensorField2 
FROM sensorTable s1 
WHERE timestamp = (SELECT MAX(timestamp) FROM sensorTable s2 WHERE s1.sensorID = s2.sensorID) 
GROUP BY sensorID; 

abbastanza autoesplicativo credo, ma here's ulteriori informazioni, se lo si desidera, così come altri esempi. È dal manuale MySQL, ma la query sopra funziona con ogni RDBMS (implementando lo standard sql'92).

+0

Il mio preferito finora. Per me si legge meglio. –

5

questo può de fatto in modo relativamente elegante utilizzando SELECT DISTINCT, come segue:

SELECT DISTINCT ON (sensorID) 
sensorID, timestamp, sensorField1, sensorField2 
FROM sensorTable 
ORDER BY sensorID, timestamp DESC; 

I lavori di cui sopra per PostgreSQL (qualche info in più here), ma credo che anche altri motori. Nel caso in cui non sia ovvio, ciò che fa è ordinare la tabella in base all'ID del sensore e al timestamp (dal più recente al più vecchio), quindi restituisce la prima riga (cioè il timestamp più recente) per ciascun ID univoco del sensore.

Nel mio caso di utilizzo ho letture di ~ 10M da sensori ~ 1K, quindi provare ad unire la tabella con se stesso su un filtro basato su timestamp è molto dispendioso in termini di risorse; quanto sopra richiede un paio di secondi.

Problemi correlati