MySQL时间序列操作
Posted
技术标签:
【中文标题】MySQL时间序列操作【英文标题】:MySQL Time Series Operation 【发布时间】:2018-05-22 08:32:20 【问题描述】:我在 5.6 版中使用 mysql。 (提到是因为不存在可用的mysql函数)
给定一个带有“运动检测”时间戳的表格。每一行都检测到运动。没有动作就意味着没有进入。
Id | Date Time
---------------
1 | 2018-01-01 15:00:01 // Start of activity phase 1
2 | 2018-01-01 15:00:03
3 | 2018-01-01 15:00:06 // Stop of activity phase 1
// Non-Activity phase
4 | 2018-01-01 17:01:06 // Start of activity phase 2
5 | 2018-01-01 17:02:06
6 | 2018-01-01 17:02:09 // Stop of activity phase 2
// Non-Activity phase, big one because of holiday
7 | 2018-01-10 19:40:06 // Start of activity phase 3
8 | 2018-01-10 19:41:06 // Stop of activity phase 3
我很难找到一个 SQL 查询来概述“活动阶段”。
我试图得到的是这样的:
Id | Activity starts | Activity ends
---------------------------------------------
1 | 2018-01-01 15:00:01 | 2018-01-01 15:00:06
2 | 2018-01-01 17:01:06 | 2018-01-01 17:02:09
3 | 2018-01-10 19:40:06 | 2018-01-10 19:41:06
我想查看“活动阶段”的开始和结束时间戳。 “活动阶段”的定义:“活动阶段”位于(例如)至少两个“非活动阶段”之间30 分钟。
提前谢谢你。
【问题讨论】:
在哪里可以阅读与 id / 日期时间相关的活动阶段? 嗨,我所拥有的只是清单 1 中显示的数据,我正在寻找一个产生如清单 2 所示内容的查询。 在您的列表 1 中,只有关于活动阶段的信息是您的评论。使用这些数据无法实现目标 再次感谢,我认为这个问题没有被理解或解释得不好。谈到清单 1:ID 3 和 4 之间没有其他行。因此,在 2018-01-01 15:00:06 和 2018-01-01 17:01:06 之间没有检测到任何动作。这是一个非活动阶段,因为它超过 30 分钟。活动阶段是不包含 (row:row+1) >= 30 分钟的所有内容。 【参考方案1】:我认为在 MySQL 5.6 中实现这一点的唯一方法是使用存储过程(尽管我很乐意看到有人证明我错了)。这个会做你想做的。请注意,它返回许多单行结果集,因此您需要在应用程序框架中处理这些结果集。或者,您可以修改过程以将中间结果存储到临时表中,然后在过程结束时将临时表中的所有内容都存储在 SELECT
中(见下文)。
DELIMITER //
DROP PROCEDURE IF EXISTS get_activity //
CREATE PROCEDURE get_activity()
BEGIN
DECLARE start, thistime, lasttime DATETIME;
DECLARE activity_count INT DEFAULT 1;
DECLARE finished INT DEFAULT 0;
DECLARE activity_cursor CURSOR FOR SELECT atime FROM activity;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET finished = 1;
OPEN activity_cursor;
FETCH activity_cursor INTO start;
SET lasttime = start;
act_loop: LOOP
FETCH activity_cursor INTO thistime;
IF finished = 1 THEN
SELECT activity_count, start, lasttime AS end;
LEAVE act_loop;
END IF;
IF thistime > lasttime + INTERVAL 30 MINUTE THEN
SELECT activity_count, start, lasttime AS end;
SET start = thistime;
SET activity_count = activity_count + 1;
END IF;
SET lasttime = thistime;
END LOOP;
END //
对于您的示例数据,此过程返回:
activity_count start end
1 2018-01-01 15:00:01 2018-01-01 15:00:06
activity_count start end
2 2018-01-01 17:01:06 2018-01-01 17:02:09
activity_count start end
3 2018-01-10 19:40:06 2018-01-10 19:41:06
这是使用临时表的过程:
DELIMITER //
DROP PROCEDURE IF EXISTS get_activity //
CREATE PROCEDURE get_activity()
BEGIN
DECLARE start, thistime, lasttime DATETIME;
DECLARE activity_count INT DEFAULT 1;
DECLARE finished INT DEFAULT 0;
DECLARE activity_cursor CURSOR FOR SELECT atime FROM activity;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET finished = 1;
CREATE TEMPORARY TABLE activity_temp (id INT, start DATETIME, end DATETIME);
OPEN activity_cursor;
FETCH activity_cursor INTO start;
SET lasttime = start;
act_loop: LOOP
FETCH activity_cursor INTO thistime;
IF finished = 1 THEN
INSERT INTO activity_temp VALUES (activity_count, start, lasttime);
LEAVE act_loop;
END IF;
IF thistime > lasttime + INTERVAL 30 MINUTE THEN
INSERT INTO activity_temp VALUES (activity_count, start, lasttime);
SET start = thistime;
SET activity_count = activity_count + 1;
END IF;
SET lasttime = thistime;
END LOOP;
SELECT * FROM activity_temp;
DROP TABLE activity_temp;
END //
输出(来自CALL get_activity()
):
id start end
1 2018-01-01 15:00:01 2018-01-01 15:00:06
2 2018-01-01 17:01:06 2018-01-01 17:02:09
3 2018-01-10 19:40:06 2018-01-10 19:41:06
【讨论】:
感谢您的详细解答,我会在接下来的 10 小时内试用。【参考方案2】:尽管@Nick 给出了有效的答案,但我还是花了几个小时才找到以下解决方案:
SET @row_number = 0;
SET @row_number2 = 0;
SET @gap_time = "00:30:00";
Select
"",
DAYNAME(prep1.datetime2) "Weekday",
prep1.datetime2 "Activity starts",
prep2.datetime1 "Activity ends"
FROM
(SELECT
(@row_number:=@row_number + 1) AS num
,detection1.id id1
,detection1.insert_datetime datetime1
,detection2.id id2
,detection2.insert_datetime datetime2
,timediff(detection2.insert_datetime,detection1.insert_datetime) as diff
FROM
MOVEMENT_TRACKING detection1,
MOVEMENT_TRACKING detection2
WHERE
detection1.id + 1 = detection2.id
and timediff(detection2.insert_datetime,detection1.insert_datetime) > @gap_time
order by detection1.id) as prep1,
(SELECT
(@row_number2:=@row_number2 + 1) AS num
,detection1.id id1
,detection1.insert_datetime datetime1
,detection2.id id2
,detection2.insert_datetime datetime2
,timediff(detection2.insert_datetime,detection1.insert_datetime) as diff
FROM
MOVEMENT_TRACKING detection1,
MOVEMENT_TRACKING detection2
WHERE
detection1.id + 1 = detection2.id
and timediff(detection2.insert_datetime,detection1.insert_datetime) > @gap_time
order by detection1.id) as prep2
WHERE
prep1.num + 1 = prep2.num
ORDER BY
prep1.datetime2 DESC
一些减少仍然是可能的,由于调试原因,我让它们进入。需要一些技巧,例如
Select ""
因为数据库托管安全警察规则。
【讨论】:
这看起来像我最初走的路。当在一个活动阶段检测到 4 个或更多动作时,我无法使其工作。在SQLFiddle 上处理您的查询,您的查询似乎也没有? 谢谢,上下边界有些问题。没有意识到这一点,但不确定对现实生活有多大影响。签出:sqlfiddle.com/#!9/02b12/1/3 很奇怪,您的 SQLFiddle 工作了四个,而我的却没有……尽管正如您所说,它缺少第一个和最后一个条目。在一张大桌子上看到两种方法之间的速度差异会很有趣;我可以看到 3 个交叉连接变得非常昂贵。以上是关于MySQL时间序列操作的主要内容,如果未能解决你的问题,请参考以下文章