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

SQL-从MySQL数据(带有时间戳和状态消息的行集)中查找所有停机时间和停机时间长度,mysql,sql,Mysql,Sql,我已经开始用循环PHP脚本监控ISP的停机时间,该脚本每5秒自动检查一次连接,并将结果存储在MySQL数据库中。脚本检查它是否能够访问几个远程网站并记录结果。检查的时间和状态始终存储在数据库中 该表的结构如下: id (auto increment) time (time stamp) status (varchar) 现在谈谈我的问题 我有数据,但我不知道如何使用它来实现我想要的结果。基本上,我想找出连接中断的所有时间段以及连接中断的时间 例如,如果我们有10行包含以下数据 0 | 2012

我已经开始用循环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的期间。因此,查询应该始终找到连接第一次断开与连接第一次再次接通之间的时间

我认为可行的一种方法是查找连接向下或向上的所有行,但如何查找这些行呢?还有比这更好的解决办法吗

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


谢谢你,尊敬的拉西

如果不只是评论,我不确定这是否有效

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

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))

我不确定这是否有效,如果不只是评论的话

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

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))

我现在真的没有时间调整它以适应您的设置,但我在网页上做了几乎相同的事情来监视计算机何时关闭,何时重新打开,然后计算它打开的总时间

我也不知道你是否可以访问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时间戳。因此,您可能需要转换日期,以便减法生效。

我现在真的没有时间将其调整为适用于您的设置,但我在网页上做了几乎相同的事情,以监控计算机何时关闭,何时重新打开,然后计算它打开的总时间

我也不知道你是否可以访问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时间戳。因此,您可能必须转换日期,以便减法有效。

这里有一种方法

首先,通过别名为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
这适用于显示的特定数据集

它没有处理它没有返回的是一个尚未结束的“下跌”期,也就是说,一系列的“下跌” 无后续“跟进”状态的状态

为了避免文件排序操作按顺序返回行,您需要一个时间、状态的覆盖索引。此查询将生成一个临时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不提供类似的功能,所以我们必须自己开发。

这里有一种方法

首先,通过别名为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
这适用于显示的特定数据集

它没有处理它没有返回的是一个尚未结束的“下跌”期,即一系列“下跌”状态,没有后续的“跟进”状态

为了避免文件排序操作按顺序返回行,您需要一个时间、状态的覆盖索引。此查询将生成一个临时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不提供任何类似的功能,因此我们必须自己动手。

一种简单的方法是,如果状态发生变化,只需插入行,但这需要您更改输入脚本。一种简单的方法是,如果状态发生变化,只需插入行,但这需要您更改输入脚本。由于某种原因,无法从查询的WHERE部分访问outerId。我发现以下错误:“where子句”@Lassi中的未知列“outerId”,我明白了,您希望修复此错误,还是坚持您接受的解决方案?然后我会删除它,以免混淆任何人如果你能修复它,我将不胜感激。公认的解决方案是可行的,但远远超出了我对MySQL的理解。我觉得你的解决方案简单多了。但如果很难让它工作,你不需要花几个小时来修复它,因为我已经有了一个工作解决方案。@Lassi我修复了它。不幸的是,我的mysql服务器坏了,所以我不能测试它。查看它是否符合您的要求。由于某种原因,无法从查询的WHERE部分访问outerId。我发现以下错误:“where子句”@Lassi中的未知列“outerId”,我明白了,您希望修复此错误,还是坚持您接受的解决方案?然后我会删除它,以免混淆任何人如果你能修复它,我将不胜感激。公认的解决方案是可行的,但远远超出了我对MySQL的理解。我觉得你的解决方案简单多了。但如果很难让它工作,你不需要花几个小时来修复它,因为我已经有了一个工作解决方案。@Lassi我修复了它。不幸的是,我的mysql服务器坏了,所以我不能测试它。看看它是否符合你的要求。