Sql 如何为每个键值选择具有最新时间戳的行?

Sql 如何为每个键值选择具有最新时间戳的行?,sql,greatest-n-per-group,Sql,Greatest N Per Group,我有一个传感器数据表。每行都有传感器id、时间戳和其他字段。我想为每个传感器选择一行最新的时间戳,包括一些其他字段 我认为解决方案是按传感器id分组,然后按max(时间戳)排序,如下所示: 这给了我一个错误,即“sensorField1必须出现在GROUPBY子句中或在聚合中使用。” 解决此问题的正确方法是什么?您只能选择组中的列或聚合函数中使用的列。您可以使用联接使其工作 select s1.* from sensorTable s1 inner join ( SELECT senso

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

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

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


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

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

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


八年后,这项法案刚刚获得通过,所以我需要指出,这是一种老办法。新方法使用
row_number()
窗口功能

您可以将表本身(在传感器id上)连接起来,并添加
left.timestamp
作为连接条件。然后选择行,其中
right.id
null
。瞧,你得到了每个传感器的最新信息

从表L中选择L.*
左连接传感器表R ON
L.传感器ID=R.传感器ID和
L.时间戳

但是请注意,如果您有少量ID和许多值,这将是非常资源密集型的!所以,我不建议将其用于某些测量材料,因为每个传感器每分钟都收集一个值。然而,在一个用例中,您需要跟踪“有时”更改的内容的“修订”,这很容易。为了完整性起见,这里有另一个可能的解决方案:

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

这可以使用
选择DISTINCT
以相对优雅的方式完成,如下所示:

SELECT DISTINCT ON (sensorID)
sensorID, timestamp, sensorField1, sensorField2 
FROM sensorTable
ORDER BY sensorID, timestamp DESC;
以上内容适用于PostgreSQL(更多信息),但我认为其他引擎也适用。在不明显的情况下,这将按照传感器ID和时间戳(从最新到最旧)对表进行排序,然后返回每个唯一传感器ID的第一行(即最新时间戳)


在我的用例中,我从~1K个传感器中读取了~10M个读数,因此尝试在基于时间戳的过滤器上加入表本身是非常耗费资源的;上面的过程需要几秒钟。

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

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

我有一个传感器表,其中有很多关于传感器的静态信息,但相关字段如下:

Sensors->(idSensor,Description,tvLastUpdate,tvLastValue,...)
tvLastupdate和tvLastValue在插入传感器读数表的触发器中设置。我总是可以直接访问这些值,而无需进行任何昂贵的查询。这会稍微去规范化。这个查询很简单:

SELECT idSensor,Description,tvLastUpdate,tvLastValue 
FROM Sensors

我对经常查询的数据使用这种方法。在我的例子中,我有一个传感器表和一个大型事件表,其中包含分钟级别的数据,数十台机器正在用这些数据更新仪表盘和图表。在我的数据场景中,触发器和缓存方法工作得很好。

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

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;

与相关子查询相比,我更准确地使用了它。请随意批评我的效率,我不太确定这方面的效果如何

您使用的是什么DB引擎?虽然下面的答案使用对Max(timestamp)值的联接应该有效,但我建议您在sensorTable上加入一个SensorReadingId(如果您在sensorTable上有一个),或者
select*from sensorTable where(sensorID,timestamp)in(按sensorID从sensorTable组中选择sensorID,Max(timestamp)
。我想“左连接”也被应用,不仅是“内部连接”;而且“和s1.timestamp=s2.mts”的一部分也不是必需的。但是,我建议在两个字段上创建索引:sensorID+timestamp-查询速度大大提高!这比其他答案快,至少在我的情况下是如此。@rain_它确实取决于用例。因此,没有“通用答案”“解决这个问题。这个解决方案真的很快。又快又容易理解。感谢您解释这个用例,因为我的非常类似。不幸的是,这不适用于MySQL()
SensorReadings->(timestamp,value,idSensor)
Sensors->(idSensor,Description,tvLastUpdate,tvLastValue,...)
SELECT idSensor,Description,tvLastUpdate,tvLastValue 
FROM Sensors
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;