2

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

例如,假设我想显示今天的统计数据。下面的代码“有效”,但在运行它并查看我所做的之后,它只是发生了可怕的胡言乱语。当我在具有数百万条索引记录的表上运行它时,它很慢www。

它现在所做的是每小时执行一次查询,直到达到 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

4 回答 4

3

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

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

然后,使用TIMEDIFFHOUR函数的组合来左连接到该表。像这样的东西(未经测试,但你明白了):

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 小时中每个小时的“原因”数。如果需要,您可以很容易地添加一些时间戳

于 2012-06-26T19:05:37.720 回答
2

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

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

c_blacklist(timestamp)如果您有一个索引,甚至更好的覆盖索引,也可能会提高(查询的)性能c_blacklist(timestamp,reason)

如果timestamp列是 datatype 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)

如果时间戳列是 datatype 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')

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

这可以通过提供一个返回“小时”的每个值的行源来解决,然后对结果集执行左连接。在以下语句中,别名为 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 将出现的三个日期替换为格式化的日期字符串。(所有三个事件都传递了相同的值。)

于 2012-06-27T00:29:46.673 回答
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'])
}
于 2012-06-26T19:10:51.323 回答
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', timestamp) 假设时间戳是纪元/unix 时间戳,它将为您提供小时。

于 2012-06-26T19:05:06.273 回答