Wie kann ich für jeden Schlüsselwert Zeilen mit dem neuesten Zeitstempel auswählen?

86

Ich habe eine Tabelle mit Sensordaten. Jede Zeile hat eine Sensor-ID, einen Zeitstempel und andere Felder. Ich möchte für jeden Sensor eine einzelne Zeile mit dem neuesten Zeitstempel auswählen, einschließlich einiger anderer Felder.

Ich dachte, dass die Lösung darin bestehen würde, nach Sensor-ID zu gruppieren und dann nach max (Zeitstempel) zu sortieren, wie folgt:

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

Dies gibt mir einen Fehler, der besagt, dass "sensorField1 in der group by-Klausel erscheinen oder in einem Aggregat verwendet werden muss".

Was ist der richtige Weg, um dieses Problem anzugehen?

ehrlich gesagt
quelle
1
Welche DB-Engine verwenden Sie?
Jürgen d
1
Während die folgenden Antworten mit JOINs für den Max-Wert (Zeitstempel) funktionieren sollten, würde ich empfehlen, sich einer SensorReadingId anzuschließen, wenn Sie eine in der sensorTable haben.
Thomas Langston

Antworten:

94

Der Vollständigkeit halber ist hier eine andere mögliche Lösung:

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

Ich denke, es ist ziemlich selbsterklärend, aber wenn Sie möchten, finden Sie hier weitere Informationen sowie weitere Beispiele. Es stammt aus dem MySQL-Handbuch, aber die obige Abfrage funktioniert mit jedem RDBMS (Implementierung des SQL'92-Standards).

schicke Hose
quelle
56

Dies kann auf relativ elegante Weise SELECT DISTINCTwie folgt erfolgen:

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

Das obige funktioniert für PostgreSQL (einige weitere Informationen hier ), aber ich denke auch andere Engines. Falls dies nicht offensichtlich ist, sortieren Sie die Tabelle nach Sensor-ID und Zeitstempel (vom neuesten zum ältesten) und geben dann die erste Zeile (dh den neuesten Zeitstempel) für jede eindeutige Sensor-ID zurück.

In meinem Anwendungsfall habe ich ~ 10 Millionen Messwerte von ~ 1K-Sensoren, daher ist der Versuch, die Tabelle mit einem zeitstempelbasierten Filter mit sich selbst zu verbinden, sehr ressourcenintensiv. Das oben genannte dauert einige Sekunden.

Svet
quelle
Diese Lösung ist sehr schnell.
Ena
Schnell und leicht zu verstehen. Vielen Dank, dass Sie auch den Anwendungsfall erklärt haben, da meiner ziemlich ähnlich ist.
Stef Verdonk
Leider funktioniert dies nicht für MySQL ( Link )
Silentsurfer
21

Sie können die Tabelle mit sich selbst verbinden (auf Sensor-ID) und left.timestamp < right.timestampals Verknüpfungsbedingung hinzufügen . Dann wählen Sie die Zeilen aus, wo right.idist null. Voila, du hast den neuesten Eintrag pro Sensor.

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)

Beachten Sie jedoch, dass dies sehr ressourcenintensiv ist, wenn Sie eine kleine Anzahl von IDs und viele Werte haben! Daher würde ich dies nicht für eine Art Messgerät empfehlen, bei dem jeder Sensor jede Minute einen Wert erfasst. In einem Anwendungsfall, in dem Sie "Revisionen" von etwas verfolgen müssen, das sich nur "manchmal" ändert, ist dies jedoch einfach.

dognose
quelle
Dies ist zumindest in meinem Fall schneller als andere Antworten.
rain_
@rain_ Es kommt wirklich auf den Anwendungsfall an. Daher gibt es keine "universelle Antwort" auf diese Frage.
Dognose
19

Sie können nur Spalten auswählen, die sich in der Gruppe befinden oder in einer Aggregatfunktion verwendet werden. Sie können einen Join verwenden, um dies zum Laufen zu bringen

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
Jürgen d
quelle
... oder select * from sensorTable where (sensorID, timestamp) in (select sensorID, max(timestamp) from sensorTable group by sensorID).
Arjan
Ich denke, "LEFT JOIN" wird ebenfalls angewendet, nicht nur "INNER JOIN"; und ein Teil "und s1.timestamp = s2.mts" ist meiner Meinung nach nicht notwendig. Und dennoch empfehle ich, einen Index für zwei Felder zu erstellen: sensorID + timestamp - die Abfragegeschwindigkeit steigt erheblich!
Igor
4
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
Joel Coehoorn
quelle
2

Es gibt eine allgemeine Antwort, die ich hier noch nicht gesehen habe, nämlich die Fensterfunktion. Es ist eine Alternative zur korrelierten Unterabfrage, wenn Ihre Datenbank dies unterstützt.

SELECT sensorID,timestamp,sensorField1,sensorField2 
FROM (
    SELECT sensorID,timestamp,sensorField1,sensorField2
        , ROW_NUMBER() OVER(
            PARTITION BY sensorID
            ORDER BY timestamp
        ) AS rn
    FROM sensorTable s1
WHERE rn = 1
ORDER BY sensorID, timestamp;

Ich verwende dies tatsächlich mehr als korrelierte Unterabfragen. Fühlen Sie sich frei, mich in den Kommentaren über die Effizienz zu überfallen, ich bin mir nicht sicher, wie es sich in dieser Hinsicht stapelt.

Jamie Marshall
quelle
0

Ich hatte größtenteils das gleiche Problem und fand eine andere Lösung, die es einfach macht, diese Art von Problem abzufragen.

Ich habe eine Tabelle mit Sensordaten (1-Minuten-Daten von ca. 30 Sensoren)

SensorReadings->(timestamp,value,idSensor)

und ich habe eine Sensortabelle, die viele meist statische Informationen über den Sensor enthält, aber die relevanten Felder sind folgende:

Sensors->(idSensor,Description,tvLastUpdate,tvLastValue,...)

Das tvLastupdate und das tvLastValue werden bei Einfügungen in die SensorReadings-Tabelle in einem Trigger festgelegt. Ich habe immer direkten Zugriff auf diese Werte, ohne teure Abfragen durchführen zu müssen. Dies denormalisiert leicht. Die Abfrage ist trivial:

SELECT idSensor,Description,tvLastUpdate,tvLastValue 
FROM Sensors

Ich verwende diese Methode für Daten, die häufig abgefragt werden. In meinem Fall habe ich eine Sensortabelle und eine große Ereignistabelle, in der Daten auf Minutenebene eingehen, UND Dutzende von Maschinen aktualisieren Dashboards und Diagramme mit diesen Daten. In meinem Datenszenario funktioniert die Trigger-and-Cache-Methode gut.

Hucker
quelle