2

特定の時間範囲内の mysql テーブルからのレコードの量を時間ごとに分類して表示する PHP のグラフのデータを生成しようとしています。各レコードには UNIX タイムスタンプがあります。

たとえば、今日の統計を表示したいとします。以下のコードは「動作します」が、それを実行して私が行ったことを見た後、たまたま動作するのは恐ろしい意味不明です。何百万ものインデックス付きレコードを含むテーブルでこれを実行すると、遅くなりますwww.

現在は、24 時間に達するまで 1 時間ごとにクエリを実行しています。問題は、同時に最大 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 で 1 時間ごとに分割してデータセットに挿入する方法はありますか? 私はとても混乱していて、どんな助けにも感謝しています。ありがとう。

4

4 に答える 4

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 行が出力されます。必要に応じて、タイムスタンプを簡単に追加できます

于 2012-06-26T19:05:37.720 に答える
2

すべての詳細行をプルバックしてクライアント側でカウントを行うよりも、データベースからカウントを返す方がはるかに高速です。

また、1 回のクエリで 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 としてエイリアス化されたサブクエリは、1 時間ごとに 1 行ずつ、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

確かに、これは現在よりもはるかに多くのクエリ テキストです。

これをコードに組み込むには、日付リテラルの 3 つの出現箇所を「%s」に置き換え、sprintf を使用して 3 つの出現箇所を書式設定された日付文字列に置き換えます。(3 つのオカレンスすべてに同じ値が渡されます。)

于 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', タイムスタンプ) を追加しました。

于 2012-06-26T19:05:06.273 に答える