如何从mysql表中显示每小时统计信息

时间:2012-06-26 18:58:19

标签: php mysql date time

我正在尝试为PHP中的图形生成数据,该图形显示在每小时细分的特定时间范围内来自mysql表的记录数量。每条记录都有一个unix时间戳。

例如,假设我想显示今天的统计数据。下面的代码“有效”,但在运行它并查看我所做的事情之后,这恰好是可怕的胡言乱语。当我在一个包含数百万个索引记录的表上运行它时,它很慢。

它现在所做的是每小时执行一次查询,直到24小时。问题是我正在尝试同时从多达10个其他表中提取数据。这意味着我可以在每个页面加载时运行多达240个查询,这是不好的。

$c = '0';
$h = '1';
while($h < 25){
    $hr_start = 3600 * $c;
    $hr_stop = 3600 * $h;
    $query = "SELECT `reason`,`timestamp`
    FROM `c_blacklist` 
    WHERE `timestamp` > '".strtotime('today')."'  + ".$hr_start." AND `timestamp` < '".strtotime('today')."' + ".$hr_stop." AND `reason` = 'hardbounce'";
    $result = mysql_query($query) or die(mysql_error());
    $hardbounce_count = mysql_num_rows($result);
    $dataset5[] = array($h,$hardbounce_count);
    $h++;
    $c++;
}

我知道有更好的方法可以做到这一点,而我却无法找到有关它的更多信息。有没有办法运行1个查询然后让PHP按小时分解并插入到数据集中?我很困惑,我感谢任何帮助。感谢。

4 个答案:

答案 0 :(得分:3)

您可以创建一种“报告查询”,在调用时,会提供最近24小时的数据。

第一步是创建一个包含24行的参考表,其中包含数字1-24(或0-23,具体取决于您的逻辑)。我将此表称为hours。通过使用此参考表,如果在给定小时内未发生任何活动,您仍将获得0计数。这与在时间戳上只执行GROUP BY的方法不同。

然后,将TIMEDIFFHOUR函数组合使用 left join 到此表。这样的事情(未经测试,但你明白了):

SELECT
    COUNT(c_blacklist.reason) as num_reasons,
    hours.hour as hour
FROM hours
LEFT JOIN c_blacklist
   ON HOUR(TIMEDIFF(now(), c_blacklist.timestamp)) = hours.hour
GROUP BY hours.hour

这将输出24行,其中包含过去24小时内每个“原因”的数量。如果需要,你可以很容易地添加一些时间戳

答案 1 :(得分:2)

让数据库返回计数会快得多,而不是拉回所有细节行并在客户端进行计数。

您可以在一次查询中将计数拉出整整24小时,这样(可能)比进行24次往返数据库获取个人计数的效率要高得多。

如果c_blacklist(timestamp)上有索引,或者甚至更好,c_blacklist(timestamp,reason)上的覆盖索引,也可能会提高(查询的)性能。

如果timestamp列的数据类型为TIMESTAMP,那么我们可以通过一些简单的算法来推导出“小时”,并按每个“小时”计算一次。

SELECT FROM_UNIXTIME((UNIX_TIMESTAMP(cb.`timestamp`) DIV 3600) * 3600) AS `cb_hour`
     , COUNT(1) AS cb_count
  FROM `c_blacklist` cb
 WHERE cb.`timestamp` >= DATE_ADD('2012-06-26 18:00',INTERVAL -1 DAY)
   AND cb.`timestamp` <  '2012-06-26 18:00'
   AND cb.`reason` = 'hardbounce'
 GROUP BY FROM_UNIXTIME((UNIX_TIMESTAMP(cb.`timestamp`) DIV 3600) * 3600)
 ORDER BY FROM_UNIXTIME((UNIX_TIMESTAMP(cb.`timestamp`) DIV 3600) * 3600)

如果timestamp列的数据类型为DATETIME,则使用其他表达式获取小时可能会更快:

SELECT DATE_FORMAT(cb.`timestamp`,'%Y-%m-%d %H:00:00') AS `cb_hour`
     , COUNT(1) AS cb_count
  FROM `c_blacklist` cb
 WHERE cb.`timestamp` >= DATE_ADD('2012-06-26 18:00',INTERVAL -1 DAY)
   AND cb.`timestamp` <  '2012-06-26 18:00'
 GROUP BY DATE_FORMAT(cb.`timestamp`,'%Y-%m-%d %H:00:00')
 ORDER BY DATE_FORMAT(cb.`timestamp`,'%Y-%m-%d %H:00:00')

此查询将有“间隙”,其中没有要计算的行,也就是说,它们不会返回零计数。

可以通过提供返回“hour”的每个值的行源,然后使用结果集执行左连接来解决这个问题。在以下语句中,子查询别名为h,返回24行,每小时一行。我们将其用作针对“结果”查询(从上面)的左连接的驱动行源。我们没有匹配的任何地方,我们将得到一个NULL计数。我们可以通过简单的函数调用将NULL替换为零。

SELECT h.hour AS cb_hour
     , IFNULL(c.cb_count,0) AS cb_count
  FROM (SELECT DATE_ADD('2012-06-26 18:00',INTERVAL -1*d.i HOUR) AS `hour`
          FROM (SELECT 00 AS i UNION ALL SELECT 01 UNION ALL SELECT 02 UNION ALL SELECT 03 
                UNION ALL SELECT 04 UNION ALL SELECT 05 UNION ALL SELECT 06 UNION ALL SELECT 07 
                UNION ALL SELECT 08 UNION ALL SELECT 09 UNION ALL SELECT 10 UNION ALL SELECT 11 
                UNION ALL SELECT 12 UNION ALL SELECT 13 UNION ALL SELECT 14 UNION ALL SELECT 15 
                UNION ALL SELECT 16 UNION ALL SELECT 17 UNION ALL SELECT 18 UNION ALL SELECT 19 
                UNION ALL SELECT 20 UNION ALL SELECT 21 UNION ALL SELECT 22 UNION ALL SELECT 23 
                ORDER BY 1 DESC
               ) d
       ) h
  LEFT
  JOIN (SELECT FROM_UNIXTIME((UNIX_TIMESTAMP(cb.`timestamp`) DIV 3600) * 3600) AS `cb_hour`
             , COUNT(1) AS cb_count
          FROM `c_blacklist` cb
         WHERE cb.`timestamp` >= DATE_ADD('2012-06-26 18:00',INTERVAL -1 DAY)
           AND cb.`timestamp` < '2012-06-26 18:00'
           AND cb.`reason` = 'hardbounce'
         GROUP BY FROM_UNIXTIME((UNIX_TIMESTAMP(cb.`timestamp`) DIV 3600) * 3600)
         ORDER BY FROM_UNIXTIME((UNIX_TIMESTAMP(cb.`timestamp`) DIV 3600) * 3600)
       ) c
    ON c.cb_hour = h.hour
 ORDER BY h.hour

当然,这比现在的查询文本要多得多。

为了将其添加到我的代码中,我将使用'%s'替换三次出现的日期文字,并使用sprintf将三次出现替换为格式化的日期字符串。 (所有三次出现都传递相同的值。)

答案 2 :(得分:1)

按时间戳的小时值分组。

SELECT
    date_format(`timestamp`,'%H') day_hour,
    count(*) count
FROM
    `c_blacklist`
WHERE
    `timestamp` between $start and $end
    and `reason` = 'hardbounce'
GROUP BY
    date_format(`timestamp`,'%H')
ORDER BY
    1;

$result = mysql_query($query) or die(mysql_error());
foreach($row = mysql_fetch_array($result)) {
    $dataset5[] = array($row['day_hour'],$row['count'])
}

答案 3 :(得分:0)

$query = "SELECT `reason`,`timestamp`,FROM_UNIXTIME(timestamp, '%H') as Hour
FROM `c_blacklist` 
WHERE `timestamp` > ('".strtotime('today')."'  + ".$hr_start.") AND (`timestamp` < '".strtotime('today')."' + ".$hr_stop.") AND `reason` = 'hardbounce'
GROUP BY FROM_UNIXTIME(timestamp, '%H')";

为操作保护顺序添加了一些(),但添加了一个FROM_UNIXTIME('%H',时间戳),它会给你一小时假设时间戳是一个epoch / unix时间戳。