3

これが私のシナリオです。イベントを含むテーブルがあり、すべてのイベントには、そのイベントが作成されたタイムスタンプを持つ「created」というフィールドがあります。ここで、イベントを新しいものから古いものに並べ替える必要がありますが、MySQLがすべてを返すようにはしたくありません。たとえば24時間の範囲で、特定の間隔の最新のものだけが必要です(編集:24時間の範囲だけでなく、おそらく数時間ごとに、柔軟なソリューションが必要です)。そして、私は過去10日間だけ必要です。私はそれを達成しましたが、可能な限り最も非効率的な方法、つまり、次のような方法で確信しています。

$timestamp = time();

for($i = 0; $i < 10; $i++) {
    $query = "SELECT * FROM `eventos` WHERE ... AND `created` < '{$timestamp}' ORDER BY `created` DESC LIMIT 1";    
    $return = $database->query( $query );

    if($database->num( $return ) > 0) {
        $event = $database->fetch( $return );
        $events[] = $event;

        $timestamp = $timestamp - 86400;
    }
}

私は十分に明確だったと思います。ありがとう、イエス。

4

6 に答える 6

3

先頭列が のインデックスがある場合created、MySQL はリバース スキャンを実行できる場合があります。イベントのない 24 時間の期間がある場合、その期間以外の行を返す可能性があります。createdその期間に行を取得していることを確認するには、次のように、列にも下限を含める必要があります。

SELECT * FROM `eventos`
 WHERE ... 
   AND `created` <  FROM_UNIXTIME( {$timestamp} )
   AND `created` >= DATE_ADD(FROM_UNIXTIME( {$timestamp} ),INTERVAL -24 HOUR)
 ORDER BY `created` DESC
 LIMIT 1

ここでのパフォーマンスの大きな鍵はcreated、WHERE 句で参照される他のすべて (またはほとんど) の列と共に、先頭の列としてインデックスを使用し、インデックスがクエリで使用されていることを確認することだと思います。

秒単位まで異なる時間間隔が必要な場合、このアプローチは簡単に一般化できます。

SELECT * FROM `eventos`
 WHERE ... 
   AND `created` <  DATE_ADD(FROM_UNIXTIME({$timestamp}),INTERVAL  0*{$nsecs} SECOND)
   AND `created` >= DATE_ADD(FROM_UNIXTIME({$timestamp}),INTERVAL -1*{$nsecs} SECOND)
 ORDER BY `created` DESC
 LIMIT 1

コードから、24 時間の期間が任意の時間に制限されているように見えます...時間関数がたとえば 1341580800 ('2012-07-06 13:20') を返す場合、10 の期間はすべて 13 からになります。当日20時~翌日13時20分。

(注: パラメータが UNIX タイムスタンプ整数である場合、これがデータベースによって正しく解釈されていることを確認してください。)

1 回のクエリで 10 行を取得する方が効率的かもしれません。「タイムスタンプ」が一意であることが保証されている場合、そのようなクエリを作成することは可能ですが、クエリ テキストは現在のものよりもかなり複雑になります。各期間内に MAX(timestamp_) を取得し、それを元に戻して行を取得することを台無しにすることはできますが、それは非常に面倒です。

10 行すべてを取得しようとする場合はUNION ALL、あまりきれいではないアプローチを試してみますが、少なくとも調整は可能です。

SELECT p0.*
  FROM ( SELECT * FROM `eventos` WHERE ... 
            AND `created` <  DATE_ADD(FROM_UNIXTIME({$timestamp}),INTERVAL  0*24 HOUR)
            AND `created` >= DATE_ADD(FROM_UNIXTIME({$timestamp}),INTERVAL -1*24 HOUR)
          ORDER BY `created` DESC LIMIT 1
       ) p0 
 UNION ALL           
SELECT p1.*
  FROM ( SELECT * FROM `eventos` WHERE ... 
            AND `created` <  DATE_ADD(FROM_UNIXTIME({$timestamp}),INTERVAL -1*24 HOUR)
            AND `created` >= DATE_ADD(FROM_UNIXTIME({$timestamp}),INTERVAL -2*24 HOUR)
          ORDER BY `created` DESC LIMIT 1
       ) p1 
 UNION ALL           
SELECT p2.*
  FROM ( SELECT * FROM `eventos` WHERE ... 
            AND `created` <  DATE_ADD(FROM_UNIXTIME({$timestamp}),INTERVAL -2*24 HOUR)
            AND `created` >= DATE_ADD(FROM_UNIXTIME({$timestamp}),INTERVAL -3*24 HOUR)
          ORDER BY `created` DESC LIMIT 1
       ) p2 
 UNION ALL           
SELECT p3.*
  FROM ...

繰り返しますが、これは一般化して、引数として秒数を渡すことができます。HOUR を SECOND に置き換え、'24' を秒数を持つバインド パラメータに置き換えます。

かなり長くなりましたが、問題なく動作するはずです。


これを 1 つの結果セットに戻すもう 1 つの非常に面倒で複雑な方法は、インライン ビューを使用して 10 期間の終了タイムスタンプを取得することです。次のようになります。

     SELECT p.period_end
       FROM (SELECT DATE_ADD(t.t_,INTERVAL -1 * i.i_* {$nsecs} SECOND) AS period_end
               FROM (SELECT FROM_UNIXTIME( {$timestamp} ) AS t_) t
               JOIN (SELECT 0 AS i_
                     UNION ALL SELECT 1
                     UNION ALL SELECT 2
                     UNION ALL SELECT 3
                     UNION ALL SELECT 4
                     UNION ALL SELECT 5
                     UNION ALL SELECT 6
                     UNION ALL SELECT 7
                     UNION ALL SELECT 8
                     UNION ALL SELECT 9
                    ) i
            ) p

そして、それをテーブルに結合します...

  ON `created` < p.period_end
 AND `created` >= DATE_ADD(p.period_end,INTERVAL -1 * {$nsecs} SECOND)

そして各期間 GROUP BY p.period_end の MAX(created) を引き戻し、それをインライン ビューでラップします。

そして、それをテーブルに結合して各行を取得します。

しかし、これは非常に厄介で、理解するのが難しく、既に行っていることよりも速く (またはより効率的に) なる可能性はほとんどありません。改善できる最大の改善点は、9 つ​​のクエリを実行するのにかかる時間です。


于 2012-07-06T18:17:43.750 に答える
1

日付(時刻ではない)である別の列を追加してから、MySQLの「groupby」を使用して各日付の最新のものを取得します。

http://www.tizag.com/mysqlTutorial/mysqlgroupby.php/

このチュートリアルはまさにそれを行いますが、日付ではなく製品タイプごとに行います。これは役立つはずです!

于 2012-07-06T18:07:02.577 に答える
1

過去 10 日間の 1 日あたりの最新の (作成日が最も大きい) イベントが必要であると仮定します。

それでは、1日あたりの最新のタイムスタンプを取得しましょう

$today = date('Y-m-d');
$tenDaysAgo = date('Y-m-d', strtotime('-10 day'));

$innerSql = "SELECT date_format(created, '%Y-%m-%d') day, MAX(created) max_created FROM eventos WHERE date_format(created, '%Y-%m-%d') BETWEEN '$today' and '$tenDaysAgo' GROUP BY date_format(created, '%Y-%m-%d')";

次に、作成された日付に一致するすべてのイベントを選択できます

$outerSql = "SELECT * FROM eventos INNER JOIN ($innerSql) as A WHERE eventos.created = A.max_created";

これをテストする機会はありませんでしたが、原則は十分に健全なはずです。

他の任意の時間数でグループ化したい場合は、innerSql を変更します。

$fromDate = '2012-07-06' // or if you want a specific time '2012-07-06 12:00:00'
$intervalInHours = 5;
$numberOfIntervals = 10;

$innerSql = "SELECT FLOOR(TIMESTAMPDIFF(HOUR, created, '$fromDate') / $intervalInHours) as grouping, MAX(created) as max_created FROM eventos WHERE created BETWEEN DATE_SUB('$fromDate', INTERVAL ($intervalInHours * $numberOfIntervals) HOUR) AND '$fromDate' GROUP BY FLOOR(TIMESTAMPDIFF(HOUR, created, '$fromDate') / $intervalInHours)";
于 2012-07-06T18:12:23.373 に答える
0

これは、過去 10 日間のその日の最初のイベントを取得するものです。

  SELECT *
    FROM eventos
   WHERE created BETWEEN DATE_SUB(DATE(NOW()), INTERVAL 10 DAY) AND DATE_ADD(DATE(NOW()), INTERVAL 1 DAY)
GROUP BY DATE(created)
ORDER BY MAX(created) DESC
   LIMIT 10
于 2012-07-06T19:04:59.910 に答える
0

これを試して:

    SELECT *
      FROM eventos
     WHERE created BETWEEN DATE_SUB(DATE(NOW()), INTERVAL 10 DAY) AND DATE_ADD(DATE(NOW()), INTERVAL 1 DAY)
  ORDER BY created DESC
     LIMIT 10
于 2012-07-06T18:51:10.257 に答える
0

10 日間にすべてのイベントが必要ですか、それとも 10 日間で 1 日 1 つのイベントだけが必要ですか?

いずれにせよ、サポートのためにMySQL の日付関数を検討してください。必要な日付範囲を取得するのに役立ちます。

于 2012-07-06T18:01:45.697 に答える