php - 在mysql中用where子句添加时间差

标签 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 并计算它或取任何平均值。

最佳答案

这可以通过对正确排序的行集使用 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) 被初始化为等同于 time 的 UNIX 时间戳,另一个用于保存之前的时间戳,以便可以计算出两者之间的差异。其他变量用于记住先前的服务器 ID 和先前的状态,以便在必要时将差异返回为 0(对记录服务器的第一个事件以及 之后的每一行都这样做)线下事件)。

您现在可以对上述查询生成的结果集进行分组,SUM() seconds_online 值并将它们除以 60 得到分钟(如果您对秒不满意),例如这个:

SELECT
  server,
  SUM(seconds_online) DIV 60 AS minutes
FROM (
  <i>the query above</i>
) s

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

解决此问题的一种方法是为每个服务器添加一行,其中包含当前时间戳和与上一条记录中相同的状态。因此,除了 server_status 之外,您还可以将以下内容作为源表:

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

UNION ALL 的左侧部分仅返回 server_status 中的所有行。右边部分首先获取每个服务器的最后一个time,然后将结果集加入到server_status中获取相应的状态,将time替换为NOW() 一路走来。

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

SELECT
  server,
  SUM(seconds_online) DIV 60 AS minutes_online
FROM (
  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
    (
      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
    ) s,
    (SELECT @lastserver := 0) x
  ORDER BY
    s.server,
    s.`time`
) s
GROUP BY
  server
;

您可以尝试(也可以玩)at SQL Fiddle也是。

关于php - 在mysql中用where子句添加时间差,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12336748/

相关文章:

php - 如何知道 Moodle Web 服务的函数参数是什么,例如 core_user_create_users

javascript - 使用javascript发送post请求

mysql全文搜索不返回任何记录

php - 我可以将几个查询替换为一个吗

php - MySQL 仅按特定行排序

javascript - 如何将带有西里尔字母符号的 php-massive 编码为 json?

php - 无法使用 PHP 表单编辑表中的数据

php - 如何向数据库中插入一些与另一行相同的信息?

sql - 在一个数据库中用SQL Server 2008中的另一个数据库中的值更新记录吗?

sql - 如何在 sql server 2008 中的 exec 语句中使用替换