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


88

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

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

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

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

この問題に取り組む正しい方法は何ですか?


1
どのDBエンジンを使用していますか?
juergen d 2013

1
Max(timestamp)値でJOINを使用する以下の回答は機能するはずですが、sensorTableにSensorReadingIdがある場合は、SensorReadingIdに参加することをお勧めします。
トーマス・ラングストン2013年

回答:


94

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

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;

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


57

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

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

上記はPostgreSQLで機能します(詳細はこちら)が、他のエンジンでも機能すると思います。不明な場合は、センサーIDとタイムスタンプ(最新から最古)でテーブルを並べ替えてから、一意のセンサーIDごとに最初の行(つまり最新のタイムスタンプ)を返します。

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


このソリューションは本当に高速です。
エナ

速くて理解しやすい。私の場合も非常に似ているので、ユースケースについても説明していただきありがとうございます。
StefVerdonk19年

1
残念ながら、これはMySQLでは機能しません(リンク
サイレントサーファー

21

テーブルをそれ自体(センサー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の量が少なく、値が多い場合、これは非常にリソースを消費することに注意してください。したがって、各センサーが毎分値を収集する、ある種の測定スタッフにはこれをお勧めしません。ただし、「時々」変更されるものの「改訂」を追跡する必要があるユースケースでは、簡単に実行できます。


少なくとも私の場合、これは他の答えよりも速いです。
rain_ 2017

@rain_それは実際にはユースケースに依存します。したがって、この質問に対する「普遍的な答え」はありません。
dognose

19

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

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

...またはselect * from sensorTable where (sensorID, timestamp) in (select sensorID, max(timestamp) from sensorTable group by sensorID)
Arjan 2016

「INNERJOIN」だけでなく「LEFTJOIN」も適用されていると思います。また、「およびs1.timestamp = s2.mts」の部分は必須のIMHOではありません。それでも、sensorID + timestampの2つのフィールドにインデックスを作成することをお勧めします-クエリ速度が大幅に向上します!
イゴール

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

2

ここでまだ見たことがない一般的な答えが1つあります。それは、ウィンドウ関数です。DBがサポートしている場合は、相関サブクエリの代わりになります。

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;

私はこれを相関サブクエリよりも実際に使用しています。効率性についてのコメントで私を破滅させてください、私はそれがその点でどのように積み重なるのかよくわかりません。


0

私はほとんど同じ問題を抱えていて、このタイプの問題を照会するのが簡単になる別の解決策になりました。

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

SensorReadings->(timestamp,value,idSensor)

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

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

tvLastupdateとtvLastValueは、SensorReadingsテーブルへの挿入時にトリガーで設定されます。高価なクエリを実行しなくても、常にこれらの値に直接アクセスできます。これはわずかに非正規化されます。クエリは簡単です。

SELECT idSensor,Description,tvLastUpdate,tvLastValue 
FROM Sensors

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.