sql >> データベース >  >> RDS >> Mysql

改善したい非常に具体的なMySQLクエリ

    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行をプルする方が効率的かもしれません。 'timestamp'が一意であることが保証されている場合、そのようなクエリを作成することは可能ですが、クエリテキストは現在のものよりもかなり複雑になります。各期間内に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つの非常に厄介で複雑な方法は、インラインビューを使用して、次のような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)
    

    そして、各期間のMAX(作成済み)を引き戻します。GROUPBY p.period_end、それをインラインビューでラップします。

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

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



    1. 異なる行への複雑なSQLステートメント

    2. SQLサーバーとmysql間のリンクサーバー上の分散トランザクション

    3. php + mysql+googleマップ

    4. MySQLデータベース内のすべてのテーブルを1つのコマンドで切り捨てますか?