使用mysql中的where子句添加时差

时间:2012-09-09 05:25:48

标签: php mysql sql timestamp

我有一个包含数十万行的数据表,这些行表示要求服务器获取数据。每条记录都有一个时间戳,服务器ID和服务器响应是否正确的二进制值(tinyint)。查询时间不是恒定的。

我试图通过将服务器在线的查询之间的时间相加来获得服务器被视为“在线”的总时间(非常可取的是mysql查询)。 例如

server | time | status
1 | 1/1/2012 11:00 online
1 | 1/1/2012 11:02 online
1 | 1/1/2012 11:05 offline
2 | 1/1/2012 11:10 online
1 | 1/1/2012 11:30 online
Time now: 11:40
Server 1 Online Time = 2+3+10 = 15 minutes

是否可以在mysql中执行此操作?我更倾向于将所有行都放到php并计算它或平均任何行。

2 个答案:

答案 0 :(得分:2)

以下是我创建的示例表结构:

-- SQL EXAMPLE
CREATE TABLE IF NOT EXISTS `stack_test` (
  `server` int(11) NOT NULL,
  `rtime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `status` tinyint(4) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
INSERT INTO `stack_test` (`server`, `rtime`, `status`) VALUES
    (1, '2012-01-01 11:00:24', 1),
    (1, '2012-01-01 11:02:24', 1),
    (1, '2012-01-01 11:05:24', 0),
    (2, '2012-01-01 11:10:24', 1),
    (1, '2012-01-01 11:30:24', 1);
-- SQL EXAMPLE END

这是PHP代码:

<?php
$query = 'SELECT DISTINCT(`server`) `server` FROM stack_test';
$res = sql::exec($query); // replace with your function/method to execute SQL
while ($row = mysql_fetch_assoc($res)) {
    $server = $row['server'];
    $uptimes = sql::exec('SELECT * FROM stack_test WHERE server=? ORDER BY rtime DESC',$server);
    $online = 0;
    $prev = time();
    $prev = strtotime('2012-01-01 11:40:00'); // just to show that it works given the example
    while ($uptime = mysql_fetch_assoc($uptimes)) {
        if ($uptime['status'] == 1) {
            echo date('g:ia',$prev) . ' to ' . date('g:ia',strtotime($uptime['rtime'])) . ' = '.(($prev-strtotime($uptime['rtime']))/60).' mins<br />';
            $online += $prev-strtotime($uptime['rtime']);
        }
        $prev = strtotime($uptime['rtime']);
    }
    echo 'Server '.$server.' is up for '.($online/60).' mins.<br />';
}
?>

这是我得到的输出:

11:40am to 11:30am = 10 mins
11:05am to 11:02am = 3 mins
11:02am to 11:00am = 2 mins
Server 1 is up for 15 mins.

11:40am to 11:10am = 30 mins
Server 2 is up for 30 mins.

答案 1 :(得分:2)

这可以使用UNIX时间戳转换和正确排序的行集上的变量赋值来完成。通过“正确排序”,我的意思是行必须按server排序,然后按time排序。以下是您可以使用变量来获取表格中每一行的上一个事件后的在线时间(间隔),以秒为单位(为此答案称为server_status):

SELECT
  *,
  @currenttime := UNIX_TIMESTAMP(`time`),
  @lasttime := CASE
    WHEN server <> @lastserver OR @laststatus = 'offline'
    THEN @currenttime
    ELSE @lasttime
  END,
  @currenttime - @lasttime AS seconds_online,
  @lasttime := @currenttime,
  @lastserver := server,
  @laststatus := status
FROM
  server_satus s,
  (SELECT @lastserver := 0) x
ORDER BY
  s.server,
  s.`time`

如您所见,临时变量(@currenttime)初始化时,UNIX时间戳等效于time,另一个用于保存上一个时间戳,以便两者之间的差异可以是计算。其他变量用于记住先前的服务器ID和先前的状态,因此,在必要时,差异返回为0(对于记录服务器的第一个事件的每一行以及{{1之后的事件)都会执行此操作。事件)。

您现在可以将上述查询生成的结果集分组,SUM()offline值并将它们除以60得到分钟(如果您对秒数不满意),如下所示:< / p>

seconds_online

但请注意,第一个查询并不真正计算服务器自上次上次事件以来在线花费的秒数。也就是说,当前时间可能与任何最新事件记录中的时间非常不同,并且不会将其考虑在内,因为查询会计算自上一行以来每行的

解决此问题的一种方法是为每个服务器添加一行,其中包含当前时间戳和与上一条记录相同的状态。因此,您将拥有以下作为源表,而不仅仅是SELECT server, SUM(seconds_online) DIV 60 AS minutes FROM ( the query above ) s

server_status

UNION ALL的左侧部分只返回SELECT server, `time`, status FROM server_status UNION ALL SELECT s.server, NOW() AS `time`, s.status FROM server_status s INNER JOIN ( SELECT server, MAX(`time`) AS last_time FROM server_status GROUP BY server ) t ON s.server = t.server AND s.`time` = t.last_time 的所有行。右侧部分首先获取每个服务器的最后server_status,然后将结果集连接到time以获取相应的状态,并在此过程中用server_status替换time

现在表格已完成,反映当前时间的“假”事件行,您可以应用第一个查询中使用的方法。这是最终查询的样子:

NOW()

你可以尝试(以及玩它)at SQL Fiddle