admin

SQL-从MySQL数据(带有时间戳和状态消息的行集)中查找所有停机时间和停机时间长度

sql

我已经开始使用循环PHP脚本监视ISP的停机时间,该脚本每5秒自动检查一次连接并将结果存储在MySQL数据库中。脚本检查它是否能够访问几个远程网站并记录结果。检查的时间和状态始终存储在数据库中。

该表的结构如下:

id (auto increment)
time (time stamp)
status (varchar)

现在到我的问题。

我有数据,但是我不知道如何使用它来达到我想要的结果。基本上,我想查找连接断开的所有时间段以及连接断开的时间。

例如,如果我们有10行包含以下数据

0 | 2012-07-24 22:23:00 | up
1 | 2012-07-24 22:23:05 | up
2 | 2012-07-24 22:23:10 | down
3 | 2012-07-24 22:23:16 | down
4 | 2012-07-24 22:23:21 | up
5 | 2012-07-24 22:23:26 | down
6 | 2012-07-24 22:23:32 | down
7 | 2012-07-24 22:23:37 | up
8 | 2012-07-24 22:23:42 | up
9 | 2012-07-24 22:23:47 | up

查询应返回句点(从22:23:10到22:23:21,以及从22:23:26到22:23:37)。因此,查询应始终查找到第一次连接断开与第一次连接断开之间的时间。

我认为可行的一种方法是查找连接断开或上升的所有行,但是如何找到这些行呢?还有没有比这更好的解决方案了?

我真的不知道查询应该是什么样子,因此将不胜感激帮助。

谢谢,谢谢拉西


阅读 158

收藏
2021-06-07

共1个答案

admin

这是一种方法。

首先按时间戳顺序获取状态行(内联视图别名为s)。然后,在处理每一行时,请使用MySQL用户变量保留前几行的值。

我们真正要寻找的是紧随一系列“下降”状态之后的“上升”状态。当我们发现该行的状态为“ up”时,我们真正需要的是前一系列“ down”状态中的最早时间戳。

因此,类似这样的方法将起作用:

SELECT d.start_down
     , d.ended_down
  FROM (SELECT @i := @i + 1 AS i
             , @start := IF(s.status = 'down' AND (@status = 'up' OR @i = 1), s.time, @start) AS start_down
             , @ended := IF(s.status = 'up' AND @status = 'down', s.time, NULL) AS ended_down
             , @status := s.status
         FROM (SELECT t.time
                    , t.status
                 FROM mydata t
                WHERE t.status IN ('up','down')
                ORDER BY t.time ASC, t.status ASC
              ) s
         JOIN (SELECT @i := 0, @status := 'up', @ended := NULL, @start := NULL) i
      ) d
WHERE d.start_down IS NOT NULL
  AND d.ended_down IS NOT NULL

这适用于您显示的特定数据集。

这不能处理的(不返回的内容)是尚未结束的“下降”时间段,即一系列“下降”状态,后面没有“上升”状态。

为避免文件排序操作按顺序返回行,您需要在上包含覆盖索引(time,status)。该查询将生成一个临时(MyISAM)表,以实现别名为的内联视图d

注意: 要了解此查询的功能,请剥离该最外面的查询,然后仅对别名为的内联视图运行查询d(可以将其添加s.time到选择列表中。)

该查询使每一行都具有“ up”或“
down”状态。“技巧”是它仅在结束“下降”时间段的行上分配“开始”和“结束”时间(标记下降时间)。(也就是说,第一行的状态为“ up”,紧随其后的状态为“
down”。)这是完成实际工作的地方,最外面的查询只是过滤掉该结果集中的所有“多余”行(不需要。)

SELECT @i := @i + 1 AS i
     , @start := IF(s.status = 'down' AND (@status = 'up' OR @i = 1), s.time, @start) AS start_down
     , @ended := IF(s.status = 'up' AND @status = 'down', s.time, NULL) AS ended_down
     , @status := s.status
     , s.time
  FROM (SELECT t.time
             , t.status
          FROM mydata t
         WHERE t.status IN ('up','down')
         ORDER BY t.time ASC, t.status ASC
       ) s
  JOIN (SELECT @i := 0, @status := 'up', @ended := NULL, @start := NULL) i

内联视图别名的目的s是按时间戳值对行进行排序,因此我们可以按顺序处理它们。内联视图的别名i就在那里,因此我们可以在查询开始时初始化一些用户变量。

如果我们在Oracle或SQL Server上运行,则可以使用“解析函数”或“排名函数”(分别命名)。MySQL不提供类似的功能,因此我们必须“自行滚动”
”。

2021-06-07