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

时间:2012-07-25 21:08:48

标签: 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)。因此,查询应始终找到第一次连接断开与第一次连接再次启动之间的时间。

我认为可以使用的一种方法是找到连接断开或关闭的所有行,但是我怎么能找到这些行呢?还有比这更好的解决方案吗?

我真的不知道查询应该是什么样的,所以非常感谢帮助。

谢谢你,问候Lassi

3 个答案:

答案 0 :(得分:0)

我不确定这是否有效(如果不仅仅是评论)

确实如此:仅当id 1小于当前id的行具有不同的状态(因此选择任何perion的第一个条目)并通过> =确定结束时间时才选择行相同的状态。

SELECT ou.id AS outerId, 
       ou.timeColumn AS currentRowTime,
       ou.status AS currentRowStatus,
      (  SELECT max(time)
         FROM statusTable
         WHERE time >= ou.timeColumn AND status = ou.status) AS endTime
FROM statusTable ou
WHERE ou.status != 
       (SELECT status
        FROM statusTable
        WHERE id = (ou.id -1))

答案 1 :(得分:0)

我现在没有时间适应这种情况来适应您的设置,但是我在网页上做的几乎是同样的事情来监控计算机何时关闭以及何时被拒绝然后计算它的总时间......

我也不知道你是否可以访问PHP,如果没有完全忽略这一点。如果你这样做,你可能会适应这样的事情:

$lasttype="OFF";
$ontime=0;
$totalontime=0;

$query2 = " SELECT
  log_unixtime,
  status
FROM somefaketablename
ORDER BY
  log_unixtime asc
;";

$result2=mysql_query($query2);
while($row2=mysql_fetch_array($result2)){
  if($lasttype=="OFF" && $row2['status']=="ON"){
    $ontime = $row2['log_unixtime'];
  }elseif($lasttype=="ON" && $row2['status']=="OFF"){
    $thisblockontime=$row2['log_unixtime']-$ontime;
    $totalontime+=($thisblockontime);
  }
  $lasttype=$row2['status'];
}

基本上,你从一个虚假的行开始,说明计算机已关闭,然后循环遍历每一行。

如果计算机已关闭,但现在处于打开状态,请设置一个变量以查看它何时打开,然后继续循环...

保持循环直到计算机开启,但现在已关闭。发生这种情况时,从当前行的时间中减去先前存储的时间。这显示了对于那组“ON”来说有多长时间。

就像我说的那样,你必须非常适应它才能让它做你想做的事情,但如果用“连接上/下”取代“电脑开/关”,它基本上是同样的想法。

使这项工作的一件事是我将日期存储为整数,作为unix时间戳。因此,您可能需要转换日期,以便减法工作。

答案 2 :(得分:0)

这是一种方法。

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

我们真正想要的是一种“向上”状态,紧跟在一系列“向下”状态之后。当我们发现具有“向上”状态的行时,我们真正需要的是前一系列“向下”状态的最早时间戳。

所以,这样的事情会奏效:

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到选择列表。)

此查询使每一行都处于“向上”或“向下”状态。 “技巧”是它仅在结束“向下”时段的行上分配“开始”和“结束”时间(标记下降时段)。 (也就是说,第一行的行处于“向上”状态后面有'向上'状态。)这是真正的工作完成的地方,最外面的查询只过滤掉了这个结果集中的所有“额外”行(我们不需要。)

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没有提供类似的东西,所以我们必须“滚动我们自己的“。