113

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

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

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

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

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

4

8 回答 8

110

为了完整起见,这是另一种可能的解决方案:

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

于 2013-06-26T19:22:56.837 回答
74

在 Postgres 中,这可以使用 以相对优雅的方式完成SELECT DISTINCT,如下所示:

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

更多信息在这里。我怀疑它也适用于其他 SQL 风格,但显然不是 MySQL(链接- 感谢@silentsurfer 的提示)

如果不明显,它的作用是按传感器 ID 和时间戳(从最新到最旧)对表进行排序,然后返回每个唯一传感器 ID 的第一行(即最新时间戳)。

在我的用例中,我从 ~1K 传感器获得 ~10M 读数,因此尝试在基于时间戳的过滤器上将表与自身连接起来非常耗费资源;以上需要几秒钟。

于 2017-07-10T17:24:36.760 回答
21

您可以将表与自身连接(在传感器 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 和许多值,这将非常耗费资源!因此,我不建议将其用于某种测量材料,因为每个传感器每分钟都会收集一个值。但是,在用例中,您需要跟踪“有时”更改的某些内容的“修订”,这很容易。

于 2013-06-26T17:55:39.720 回答
21

您只能选择组中的列或聚合函数中使用的列。您可以使用联接来使其正常工作

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
于 2013-06-26T17:50:14.803 回答
6
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

八年后,这刚刚得到了投票,所以我需要指出这是旧的做法。新方法使用row_number()窗口函数或APPLY横向连接。

于 2013-06-26T17:52:30.060 回答
3

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

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;

我实际上比相关的子查询更多地使用它。随意在关于效率的评论中抨击我,我不太确定它在这方面是如何叠加的。

于 2019-09-20T14:24:30.833 回答
0

还想使用该not exists子句给出答案:

SELECT sensorID,timestamp,sensorField1,sensorField2 
FROM sensorTable t1
where not exists
( select * from sensorTable t2 where t1.sensorId=t2.sensorId
  and t1.timestamp < t2.timestamp );

根据您的 DBMS/SQL 优化器,这可能是一个有效且不错的选择。

于 2021-11-18T10:52:55.537 回答
0

我遇到了几乎相同的问题,并最终得到了一个不同的解决方案,这使得这类问题的查询变得微不足道。

我有一张传感器数据表(来自大约 30 个传感器的 1 分钟数据)

SensorReadings->(timestamp,value,idSensor)

我有一个传感器表,其中包含很多关于传感器的大部分静态内容,但相关字段是:

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

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

SELECT idSensor,Description,tvLastUpdate,tvLastValue 
FROM Sensors

我将这种方法用于经常查询的数据。在我的情况下,我有一个传感器表和一个大型事件表,其中包含分钟级别的数据,并且数十台机器正在使用该数据更新仪表板和图表。在我的数据场景中,触发和缓存方法效果很好。

于 2018-03-15T02:18:08.560 回答