MySQL - 将数据点转换为范围

时间:2013-05-08 15:39:56

标签: mysql sql query-optimization relational-database

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

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

这是表格:

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的条目,尽管不太可能。

3 个答案:

答案 0 :(得分:1)

这可能更适合作为评论而非解决方案,但评论时间太长了。

您正在尝试在MySQL中实现lead()函数,遗憾的是,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;

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

答案 1 :(得分:0)

如果对主键使用auto_increment,则可以在查询条件部分中将row_id替换为time_amp。像这样:

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

答案 2 :(得分: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
             )