我有一个包含时间序列传感器数据的大表。大的是从几千到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组/ ,最后一种方法是不是很好?还是有更好的方法?是否预期相关子查询与经验一样慢?
答案 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;