如何为每个键值选择带有最近时间戳记的行?


86

我有一张传感器数据表。每行都有一个传感器ID,一个时间戳和其他字段。我想为每个传感器选择一个带有最新时间戳的单行,包括其他一些字段。

我认为解决方案是按传感器ID分组,然后按max(timestamp)排序,如下所示:

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

这给我一个错误,说“ sensorField1必须出现在group by子句中或在聚合中使用”。

解决此问题的正确方法是什么?


1
您正在使用什么数据库引擎?
juergen d

1
虽然以下对Max(timestamp)值使用JOIN的答案应该可行,但如果sensorTable上有一个,我建议加入SensorReadingId。
Thomas Langston 2013年

Answers:


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标准)。


56

可以使用SELECT DISTINCT,以相对优雅的方式完成此操作,如下所示:

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

以上适用于PostgreSQL(此处有更多信息)),但我认为其他引擎也可以。如果不是很明显,它会按照传感器ID和时间戳(最新到最旧)对表进行排序,然后返回每个唯一传感器ID的第一行(即最新时间戳)。

在我的用例中,我从〜1K传感器中获得了约1000万个读数,因此尝试在基于时间戳的过滤器上将表与自身连接起来会占用大量资源;以上需要几秒钟。


这个解决方案确实非常快。
Ena

快速且易于理解。也感谢您解释用例,因为我的情况非常相似。
Stef Verdonk

不幸的是,这不适用于MySQL(链接
–silentsurfer

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数量少且值很多,这将占用大量资源!因此,对于某种测量材料,我不推荐这样做,因为每个Sensor每分钟都会收集一个值。但是,在用例中,您需要跟踪仅在“有时”发生更改的某些内容的“修订”,这很容易进行。


至少在我看来,这比其他答案要快。
rain_

@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

我认为“ LEFT JOIN”同样适用,不仅是“ INNER JOIN”;而“ and s1.timestamp = s2.mts”部分不是必需的恕我直言。但是,我建议在两个字段上创建索引:sensorID + timestamp-查询速度会大大提高!
伊戈尔

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

我还没有看到一个常见的答案,那就是窗口函数。如果您的数据库支持,它是相关子查询的替代方法。

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,...)

在插入SensorReadings表的触发器中设置了tvLastupdate和tvLastValue。我始终可以直接访问这些值,而无需执行任何昂贵的查询。这确实会稍微不规范化。该查询很简单:

SELECT idSensor,Description,tvLastUpdate,tvLastValue 
FROM Sensors

我将这种方法用于经常查询的数据。在我的情况下,我有一个传感器表和一个大事件表,它们有微小的数据输入,并且数十台机器正在使用该数据更新仪表板和图形。对于我的数据方案,触发器和缓存方法效果很好。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.