web-dev-qa-db-ja.com

各キー値の最新のタイムスタンプを持つ行を選択するにはどうすればよいですか?

センサーデータの表があります。各行には、センサーID、タイムスタンプ、およびその他のフィールドがあります。他のフィールドの一部を含め、各センサーの最新のタイムスタンプを持つ単一の行を選択します。

解決策は、センサーIDでグループ化し、次のようにmax(timestamp)で並べ替えることだと思いました。

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

これにより、「sensorField1はgroup by句に表示されるか、集計で使用される必要がある」というエラーが表示されます。

この問題にアプローチする正しい方法は何ですか?

61
franklynd

完全を期すために、別の可能な解決策を次に示します。

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;

かなり自明だと思いますが、 here's 必要に応じて、他の例と同様に詳細をご覧ください。これはMySQLのマニュアルによるものですが、上記のクエリはすべてのRDBMS(sql'92標準を実装)で機能します。

70
fancyPants

これは、次のようにSELECT DISTINCTを使用して比較的エレガントな方法で実行できます。

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

上記はPostgreSQLで動作します(詳細は here )が、他のエンジンも考えます。明らかでない場合、これはセンサーIDとタイムスタンプ(新しいものから古いもの)でテーブルをソートし、一意のセンサーIDごとに最初の行(つまり最新のタイムスタンプ)を返します。

私のユースケースでは、〜1Kセンサーから〜1000万の読み取り値があるため、タイムスタンプベースのフィルターでテーブルをそれ自体と結合しようとすると、非常にリソースを消費します。上記には数秒かかります。

29
Svet

テーブルを(センサーIDで)自身と結合し、結合条件としてleft.timestamp < right.timestampを追加できます。次に、right.idnullである行を選択します。出来上がり、センサーごとに最新のエントリを取得しました。

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)

ただし、少量のIDと多くの値がある場合、これは非常にリソースを消費することに注意してください。そのため、各センサーが1分ごとに値を収集する、ある種の測定スタッフにはこれをお勧めしません。ただし、ユースケースでは、「時々」変化する何かの「リビジョン」を追跡する必要があるため、簡単に実行できます。

17
dognose

グループ内にあるか、集計関数で使用される列のみを選択できます。結合を使用してこれを機能させることができます

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
16
juergen d
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
3
Joel Coehoorn

@fancyPantsが答えたように

SELECT sensorID,timestamp,sensorField1,sensorField2 
FROM sensorTable stmt_outer
WHERE timestamp = (SELECT MAX(timestamp) FROM sensorTable stmt_inner WHERE outer.sensorID = inner.sensorID)

これはCorrelated Subqueriesと呼ばれ、通常のネストされたサブクエリとは異なります
i.e:各サブクエリは、外部クエリの行ごとに1回実行されます。
これは、内側のサブクエリが次のことを意味します。

(SELECT MAX(timestamp) FROM sensorTable inner WHERE outer.sensorID = inner.sensorID)

行ごとに実行され、結果として列にはmax(timestamp)が含まれます。この列は外側の列と比較され、外側のステートメントの異なるsensor_idを1つだけ選択します

1
Emad Saeed

私はほとんど同じ問題を抱えていたため、この種の問題を簡単に照会できる別の解決策を見つけました。

センサーデータのテーブルがあります(約30個のセンサーからの1分間のデータ)

SensorReadings->(timestamp,value,idSensor)

センサーに関するほとんど静的なものがたくさんあるセンサーテーブルがありますが、関連するフィールドは次のとおりです。

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

TvLastupdateおよびtvLastValueは、SensorReadingsテーブルへの挿入のトリガーで設定されます。高価なクエリを実行する必要なく、これらの値に直接アクセスできます。これはわずかに非正規化します。クエリは簡単です:

SELECT idSensor,Description,tvLastUpdate,tvLastValue 
FROM Sensors

このメソッドは、頻繁にクエリされるデータに使用します。私の場合、センサーテーブルと大きなイベントテーブルがあり、それらにはデータが分単位で入っており、数十台のマシンがそのデータでダッシュボードとグラフを更新しています。私のデータシナリオでは、トリガーとキャッシュの方法はうまく機能します。

0
Hucker