センサーデータの表があります。各行には、センサーID、タイムスタンプ、およびその他のフィールドがあります。他のフィールドの一部を含め、各センサーの最新のタイムスタンプを持つ単一の行を選択します。
解決策は、センサーIDでグループ化し、次のようにmax(timestamp)で並べ替えることだと思いました。
SELECT sensorID,timestamp,sensorField1,sensorField2
FROM sensorTable
GROUP BY sensorID
ORDER BY max(timestamp);
これにより、「sensorField1はgroup by句に表示されるか、集計で使用される必要がある」というエラーが表示されます。
この問題にアプローチする正しい方法は何ですか?
完全を期すために、別の可能な解決策を次に示します。
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標準を実装)で機能します。
これは、次のようにSELECT DISTINCT
を使用して比較的エレガントな方法で実行できます。
SELECT DISTINCT ON (sensorID)
sensorID, timestamp, sensorField1, sensorField2
FROM sensorTable
ORDER BY sensorID, timestamp DESC;
上記はPostgreSQLで動作します(詳細は here )が、他のエンジンも考えます。明らかでない場合、これはセンサーIDとタイムスタンプ(新しいものから古いもの)でテーブルをソートし、一意のセンサーIDごとに最初の行(つまり最新のタイムスタンプ)を返します。
私のユースケースでは、〜1Kセンサーから〜1000万の読み取り値があるため、タイムスタンプベースのフィルターでテーブルをそれ自体と結合しようとすると、非常にリソースを消費します。上記には数秒かかります。
テーブルを(センサーIDで)自身と結合し、結合条件としてleft.timestamp < right.timestamp
を追加できます。次に、right.id
がnull
である行を選択します。出来上がり、センサーごとに最新のエントリを取得しました。
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分ごとに値を収集する、ある種の測定スタッフにはこれをお勧めしません。ただし、ユースケースでは、「時々」変化する何かの「リビジョン」を追跡する必要があるため、簡単に実行できます。
グループ内にあるか、集計関数で使用される列のみを選択できます。結合を使用してこれを機能させることができます
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
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
@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つだけ選択します
私はほとんど同じ問題を抱えていたため、この種の問題を簡単に照会できる別の解決策を見つけました。
センサーデータのテーブルがあります(約30個のセンサーからの1分間のデータ)
SensorReadings->(timestamp,value,idSensor)
センサーに関するほとんど静的なものがたくさんあるセンサーテーブルがありますが、関連するフィールドは次のとおりです。
Sensors->(idSensor,Description,tvLastUpdate,tvLastValue,...)
TvLastupdateおよびtvLastValueは、SensorReadingsテーブルへの挿入のトリガーで設定されます。高価なクエリを実行する必要なく、これらの値に直接アクセスできます。これはわずかに非正規化します。クエリは簡単です:
SELECT idSensor,Description,tvLastUpdate,tvLastValue
FROM Sensors
このメソッドは、頻繁にクエリされるデータに使用します。私の場合、センサーテーブルと大きなイベントテーブルがあり、それらにはデータが分単位で入っており、数十台のマシンがそのデータでダッシュボードとグラフを更新しています。私のデータシナリオでは、トリガーとキャッシュの方法はうまく機能します。