2012-06-26 155 views
2

我正在嘗試爲PHP中的圖表生成數據,該圖表顯示按每小時細分的特定時間範圍內的mysql表中的記錄數量。每條記錄都有一個unix時間戳。如何顯示來自mysql表的每小時統計信息

例如,我想要顯示今天的統計信息。下面的代碼「有用」,但是在運行它並查看我所做的事情之後,發生這種情況只會是可怕的胡言亂語。當我在一張擁有數百萬索引記錄的桌子上運行它時,它很慢。

它現在執行的是每個小時的查詢,直到達到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按小時分解並插入數據集?我很困惑,我感謝任何幫助。謝謝。

+0

你可以做GROUP BY HOUR(FROM_UNIXTIME(unixtime))WHERE date = DATE(FROM_UNIXTIME(unixtime))嗎? – Kermit

+0

「c_blacklist」中名爲'timestamp'的列的數據類型是什麼?它是'TIMESTAMP'還是它是'DATETIME'?時間戳列上的WHERE子句可能不太理想。 – spencer7593

回答

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」,時間戳),這將給你小時假定時間戳是一個劃時代/ Unix時間戳。

3

你可以創建一種「報告查詢」,當被調用時,會給你最近24小時的數據。

第一步是創建一個包含數字1-24(或0-23取決於您的邏輯)的24行的參考表。我會打電話給這張表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個小時的「理由」的數量。如果您需要

+0

我傾向於同意這個概念。我們爲80,000所不同的學校記錄了數百萬視頻的每個視頻事件(播放/暫停/停止/啓動),因此需要一個隔夜流程來記錄日誌並創建歷史摘要和視圖來優化用戶想要查看的內容 – GDP

+0

+1它是將使數據庫返回COUNT的速度要快得多,而不是從數據庫中檢索所有行並在客戶端對它們進行計數。 – spencer7593

+0

+1。這是一個可行的方法。一個問題是性能,如果c_blacklist有大量行跨越較大的日期範圍。我不相信MySQL能夠利用此查詢在時間戳列上使用索引。 – spencer7593

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']) 
} 
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) 

如果時間戳列的數據類型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值。我們可以用一個簡單的函數調用將零替換爲零。

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用格式化的日期字符串替換三次出現。 (所有三次出現都通過相同的值。)