我有一张传感器数据表。每行都有一个传感器ID,一个时间戳和其他字段。我想为每个传感器选择一个具有最新时间戳的行,包括一些其他字段。
我认为解决方案是按传感器ID分组然后按max(timestamp)排序,如下所示:
SELECT sensorID,timestamp,sensorField1,sensorField2
FROM sensorTable
GROUP BY sensorID
ORDER BY max(timestamp);
这给了我一个错误,说“sensorField1必须出现在group by子句中或者在聚合中使用。”
解决此问题的正确方法是什么?
答案 0 :(得分:65)
为了完整起见,这是另一种可能的解决方案:
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标准)。
答案 1 :(得分:24)
这可以使用SELECT DISTINCT
以相对优雅的方式完成,如下所示:
SELECT DISTINCT ON (sensorID)
sensorID, timestamp, sensorField1, sensorField2
FROM sensorTable
ORDER BY sensorID, timestamp DESC;
以上适用于PostgreSQL(更多信息here),但我认为还有其他引擎。如果不是很明显,它的作用是按传感器ID和时间戳(从最新到最旧)对表进行排序,然后返回每个唯一传感器ID的第一行(即最新时间戳)。
在我的用例中,我从~1K传感器读取了大约10M的读数,因此在基于时间戳的过滤器上尝试将表连接起来是非常耗费资源的。以上需要几秒钟。
答案 2 :(得分:15)
您可以将表连接到自身(在传感器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和许多值,这将是非常耗费资源的!因此,我不建议将其用于某种测量材料,其中每个传感器每分钟收集一次值。但是在用例中,您需要跟踪“有时”更改的内容的“修订”,这很容易。
答案 3 :(得分:14)
您只能选择组中的列或在聚合函数中使用的列。您可以使用联接来实现此功能
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
答案 4 :(得分:3)
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
答案 5 :(得分:1)
@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
,它与普通的嵌套子查询不同。
即:每个子查询针对外部查询的每一行执行一次。
这意味着内部子查询:
(SELECT MAX(timestamp) FROM sensorTable inner WHERE outer.sensorID = inner.sensorID)
将针对每一行执行,导致列包含max(timestamp),然后将其与外部列进行比较以仅选择外部语句的一个不同的sensor_id
答案 6 :(得分:0)
我遇到了大部分相同的问题,最终得到了一个不同的解决方案,使得这类问题无法进行查询。
我有一张传感器数据表(来自约30个传感器的1分钟数据)
SensorReadings->(timestamp,value,idSensor)
我有一个传感器表,其中有很多关于传感器的静态内容,但相关的字段是:
Sensors->(idSensor,Description,tvLastUpdate,tvLastValue,...)
tvLastupdate和tvLastValue在插入SensorReadings表时触发器中设置。我总是可以直接访问这些值,而无需进行任何昂贵的查询。这会略微反规范化。查询很简单:
SELECT idSensor,Description,tvLastUpdate,tvLastValue
FROM Sensors
我将此方法用于经常查询的数据。在我的情况下,我有一个传感器表和一个大型事件表,其中有数据以分钟级别进入,并且有数十台机器正在使用该数据更新仪表板和图表。在我的数据场景中,触发和缓存方法运行良好。
答案 7 :(得分:0)
我还没有看到一个常见的答案,那就是窗口函数。如果您的数据库支持,它是相关子查询的替代方法。
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;
与相关子查询相比,我更多地使用它。随意在有关效率的评论中让我失望,我不太确定它在这方面如何堆叠。