0

我有一个测量数据库,指示传感器、读数和读数的时间戳。只有在发生变化时才会记录测量值。我想生成一个结果集,显示每个传感器读取特定测量值的范围。

时间戳以毫秒为单位,但我以秒为单位输出结果。

这是表格:

CREATE TABLE `raw_metric` (
    `row_id` BIGINT NOT NULL AUTO_INCREMENT,
    `sensor_id` BINARY(6) NOT NULL,
    `timestamp` BIGINT NOT NULL,
    `angle` FLOAT NOT NULL,
    PRIMARY KEY (`row_id`)
)

现在我正在使用子查询获得我想要的结果,但是当有很多数据点时它相当慢:

SELECT row_id,
    HEX(sensor_id),
    angle,
    (
        COALESCE((
                SELECT MIN(`timestamp`)
                FROM raw_metric AS rm2
                WHERE rm2.`timestamp` > rm1.`timestamp`
                    AND rm2.sensor_id = rm1.sensor_id
                ), UNIX_TIMESTAMP() * 1000) - `timestamp`
        ) / 1000 AS duration
FROM raw_metric AS rm1

本质上,要获得范围,我需要获得下一个读数(如果没有另一个读数,则使用当前时间)。子查询查找晚于当前时间戳但来自同一传感器的最小时间戳。

此查询不会经常发生,因此我宁愿不必在时间戳列上添加索引并减慢插入速度。我希望有人可能对另一种方法提出建议。

更新: row_id应该与时间戳一起增加,但由于网络延迟问题不能保证。因此,具有较低 row_id 的条目可能出现在后面的 row_id 之后,尽管不太可能。

4

3 回答 3

1

这可能更适合作为评论而不是作为解决方案,但是对于评论来说太长了。

您正在尝试lead()在 MySQL 中实现该功能,不幸的是,MySQL 没有窗口函数。您可以切换到 Oracle、DB2、Postgres、SQL Server 2012 并使用那里的内置(和优化)功能。好吧,那可能不太现实。

因此,给定您的数据结构,您需要执行相关子查询或非等值连接(实际上是部分等值连接,因为存在匹配项sensor_id)。除非您添加索引,否则这些将是昂贵的操作。除非您每秒添加数十次测量,否则索引上的额外开销应该不是什么大问题。

您还可以更改数据结构。如果您有一个“传感器计数器”,它是一个枚举读数的序列号,那么您可以将其用作等值连接(尽管为了获得良好的性能,您可能仍需要一个索引)。将其添加到您的表中需要有一个触发器——这可能比插入时的索引性能更差。

如果您只有几个传感器,您可以为每个传感器创建一个单独的表。哦,我能感觉到这个建议的呻吟。但是,如果你这样做了,那么自动递增的 id 将扮演相同的角色。老实说,如果我能数出每只手上的传感器数量,我只会这样做。

最后,我可能会建议您在插入期间接受打击,并在每条记录上设置“有效”和“结束”时间(以及传感器 id 和时间戳或 id 的索引)。使用这些额外的列,您将可能会发现桌子的更多用途。

如果您只为一个传感器执行此操作,则为信息创建一个临时表并使用自动递增的 id 列。然后将数据插入其中:

insert into temp_rawmetric (orig_row_id, sensor_id, timestamp, angle)
    select orig_row_id, sensor_id, timestamp, angle
    from raw_metric
    order by sensor_id, timestamp;

确保您的表具有temp_rawmetric_id自动递增的列和主键(自动创建索引)。order by确保根据时间戳递增。

然后你可以做你的查询:

select trm.sensor_id, trm.angle,
       trm.timestamp as startTime, trmnext.timestamp as endTime
from temp_rawmetric trm left outer join
     temp_rawmetric trmnext
     on trmnext.temp_rawmetric_id = trm.temp_rawmetric_id+1;

这将需要通过原始数据来增加数据,然后在临时表上进行主键连接。第一个可能需要一些时间。第二个应该很快。

于 2013-05-08T17:59:34.307 回答
0
Select  rm1.row_id
        ,HEX(rm1.sensor_id)
        ,rm1.angle
        ,(COALESCE(rm2.timestamp, UNIX_TIMESTAMP() * 1000) - rm1.timestamp) as duration
from    raw_metric rm1
left outer join
        raw_metric rm2
on      rm2.sensor_id = rm1.sensor_id
and     rm2.timestamp = (
             select min(timestamp) 
             from   raw_metric rm3
             where  rm3.sensor_id = rm1.sensor_id
             and    rm3.timestamp > rm1.timestamp
             )
于 2013-05-08T15:51:32.857 回答
0

如果您使用 auto_increment 作为主键,您可以在查询条件部分将时间戳替换为 row_id。像这样:

SELECT row_id,
HEX(sensor_id),
angle,
(
    COALESCE((
            SELECT MIN(`timestamp`)
            FROM raw_metric AS rm2
            WHERE rm2.`row_id` > rm1.`row_id`
                AND rm2.sensor_id = rm1.sensor_id
            ), UNIX_TIMESTAMP() * 1000) - `timestamp`
    ) / 1000 AS duration
FROM raw_metric AS rm1

它必须快速工作。

您还可以添加一个子查询以快速选择新传感器值的行 ID。看:

SELECT row_id,
HEX(sensor_id),
angle,
(
    COALESCE((
        SELECT timestamp FROM raw_metric AS rm1a
        WHERE row_id = 
        (
            SELECT MIN(`row_id`)
            FROM raw_metric AS rm2
            WHERE rm2.`row_id` > rm1.`row_id`
                AND rm2.sensor_id = rm1.sensor_id
        )
        ), UNIX_TIMESTAMP() * 1000) - `timestamp`
    ) / 1000 AS duration
FROM raw_metric AS rm1
于 2013-05-08T15:49:55.903 回答