如何找到先前的记录[n-group-max max(timestamp)<时间戳]

时间:2014-06-27 17:15:51

标签: mysql greatest-n-per-group

我有一个包含时间序列传感器数据的大表。大的是从几千到10M的记录,在被监视的各种频道之间划分。 对于某种传感器类型,我需要计算当前和先前读数之间的时间间隔,即找到当前读数之前的最大时间戳。

明显的方法浮现在脑海中,每个都在Core i5上测量40k条目的通道:

相关子查询

SELECT collect.*, prev.timestamp AS prev_timestamp
FROM data AS collect 
LEFT JOIN data AS prev ON prev.channel_id = collect.channel_id AND prev.timestamp = ( 
    SELECT MAX(timestamp) 
    FROM data 
    WHERE data.channel_id = collect.channel_id AND data.timestamp < collect.timestamp
) 
WHERE collect.channel_id=14 AND collect.timestamp >= 0 
ORDER BY collect.timestamp

时间(exec,fetch)11秒,21秒

计划

+----+--------------------+---------+------+------------------------------+---------+---------+-------------------------+-------+--------------------------+
| id |    select_type     |  table  | type |        possible_keys         |   key   | key_len |           ref           | rows  |          Extra           |
+----+--------------------+---------+------+------------------------------+---------+---------+-------------------------+-------+--------------------------+
|  1 | PRIMARY            | collect | ref  | ts_uniq,IDX_ADF3F36372F5A1AA | ts_uniq |       5 | const                   | 45820 | Using where              |
|  1 | PRIMARY            | prev    | ref  | ts_uniq,IDX_ADF3F36372F5A1AA | ts_uniq |      13 | const,func              |     1 | Using index              |
|  2 | DEPENDENT SUBQUERY | data    | ref  | ts_uniq,IDX_ADF3F36372F5A1AA | ts_uniq |       5 | nils.collect.channel_id |  2495 | Using where; Using index |
+----+--------------------+---------+------+------------------------------+---------+---------+-------------------------+-------+--------------------------+

反加入

SELECT d1.*, d2.timestamp AS prev_timestamp
FROM data d1
LEFT JOIN data d2 ON
    d2.channel_id=14 AND
    d2.timestamp < d1.timestamp 
LEFT JOIN data d3 ON
    d3.channel_id=14 AND
    d3.timestamp < d1.timestamp AND
    d3.timestamp > d2.timestamp
WHERE 
    d3.timestamp IS NULL AND
    d1.channel_id=14
ORDER BY timestamp

时间 12秒,21秒

计划

+----+-------------+-------+------+------------------------------+---------+---------+-------+-------+--------------------------------------+
| id | select_type | table | type |        possible_keys         |   key   | key_len |  ref  | rows  |                Extra                 |
+----+-------------+-------+------+------------------------------+---------+---------+-------+-------+--------------------------------------+
|  1 | SIMPLE      | d1    | ref  | ts_uniq,IDX_ADF3F36372F5A1AA | ts_uniq |       5 | const | 45820 | Using where                          |
|  1 | SIMPLE      | d2    | ref  | ts_uniq,IDX_ADF3F36372F5A1AA | ts_uniq |       5 | const | 47194 | Using index                          |
|  1 | SIMPLE      | d3    | ref  | ts_uniq,IDX_ADF3F36372F5A1AA | ts_uniq |       5 | const | 47194 | Using where; Using index; Not exists |
+----+-------------+-------+------+------------------------------+---------+---------+-------+-------+--------------------------------------+

我已经想出了另一种模式,我打电话给天真的计数

SELECT current.*, prev.timestamp AS prev_timestamp FROM
(
    SELECT data.*, @r1 := @r1+1 AS rownum from data
    CROSS JOIN (SELECT @r1 := 0) AS vars 
    WHERE channel_id=14
    ORDER BY timestamp
) AS current
LEFT JOIN
(
    SELECT data.*, @r2 := @r2+1 AS rownum from data
    CROSS JOIN (SELECT @r2 := 0) AS vars 
    WHERE channel_id=14
    ORDER BY timestamp
) AS prev
ON current.rownum = prev.rownum+1

时间 1.1秒(这个实际上最快!)

计划

+----+-------------+------------+--------+------------------------------+---------+---------+-----+-------+----------------+
| id | select_type |   table    |  type  |        possible_keys         |   key   | key_len | ref | rows  |     Extra      |
+----+-------------+------------+--------+------------------------------+---------+---------+-----+-------+----------------+
|  1 | PRIMARY     | <derived2> | ALL    |                              |         |         |     | 24475 |                |
|  1 | PRIMARY     | <derived4> | ALL    |                              |         |         |     | 24475 |                |
|  4 | DERIVED     | <derived5> | system |                              |         |         |     |     1 |                |
|  4 | DERIVED     | data       | ref    | ts_uniq,IDX_ADF3F36372F5A1AA | ts_uniq |       5 |     | 45820 | Using where    |
|  5 | DERIVED     |            |        |                              |         |         |     |       | No tables used |
|  2 | DERIVED     | <derived3> | system |                              |         |         |     |     1 |                |
|  2 | DERIVED     | data       | ref    | ts_uniq,IDX_ADF3F36372F5A1AA | ts_uniq |       5 |     | 45820 | Using where    |
|  3 | DERIVED     |            |        |                              |         |         |     |       | No tables used |
+----+-------------+------------+--------+------------------------------+---------+---------+-----+-------+----------------+

由于查询可能在小型平台上运行,例如RasPi性能至关重要 - 几秒钟是最可接受的。

我的问题:对于最大n组/ ,最后一种方法是不是很好?还是有更好的方法?是否预期相关子查询与经验一样慢?

1 个答案:

答案 0 :(得分:1)

变量的最后一种方法是合理的。您也可以尝试:

SELECT collect.*,
       (select max(timestamp)
        from data
        where data.channel_id = collect.channel_id AND data.timestamp < collect.timestamp
       ) AS prev_timestamp
FROM data AS collect 
WHERE collect.channel_id = 14 AND collect.timestamp >= 0 
ORDER BY collect.timestamp;

此外,在:collect(channel_id,timestamp)上创建索引。

编辑:

以下可能是最快的:

  select d.*,
         if(@channel_id = channel_id, @prev_timestamp, NULL) as prev_timestamp,
         @channel_id := channel_id, @prev_timestamp = timestamp
  from data d cross join
       (select @channel_id := 0, @prev_timestamp := 0) vars
  where collect.channel_id = 14 AND collect.timestamp >= 0 
  order by channel_id, timestamp;