3

必要なものを正確に返す SQL クエリ (以下を参照) がありますが、phpMyAdmin を実行すると、0.0009 秒から 0.1149 秒、場合によっては最大 7.4983 秒かかります。

クエリ:

SELECT
  e.id,
  e.title,
  e.special_flag,
  CASE WHEN a.date >= '2013-03-29' THEN a.date ELSE '9999-99-99' END as date
  CASE WHEN a.date >= '2013-03-29' THEN a.time ELSE '99-99-99' END as time,
  cat.lastname,
  FROM e_table as e
  LEFT JOIN a_table as a ON (a.e_id=e.id)
  LEFT JOIN c_table as c ON (e.c_id=c.id)
  LEFT JOIN cat_table as cat ON (cat.id=e.cat_id)
  LEFT JOIN m_table as m ON (cat.name=m.name AND cat.lastname=m.lastname)
  JOIN (
          SELECT DISTINCT innere.id
          FROM e_table as innere
          LEFT JOIN a_table as innera ON (innera.e_id=innere.id AND
                                          innera.date >= '2013-03-29')
          LEFT JOIN c_table as innerc ON (innere.c_id=innerc.id)
          WHERE (
                  (
                    innera.date >= '2013-03-29' AND 
                    innera.flag_two=1
                  ) OR 
                  innere.special_flag=1
                ) AND
                innere.flag_three=1 AND 
                innere.flag_four=1
          ORDER BY COALESCE(innera.date, '9999-99-99') ASC,
                   innera.time ASC,
                   innere.id DESC LIMIT 0, 10
       ) AS elist ON (e.id=elist.id)
  WHERE (a.flag_two=1 OR e.special_flag) AND e.flag_three=1 AND e.flag_four=1
  ORDER BY a.date ASC, a.time ASC, e.id DESC

プランの説明: 上記のクエリ説明プラン

問題は、このクエリのどの部分がパフォーマンスに大きな違いを引き起こしている可能性があるかということです。

4

2 に答える 2

6

あなたの質問に具体的に答えるには、広範囲のパフォーマンスを引き起こしているのは、クエリの特定の部分ではありません。これは、MySQL がやるべきことを行っていることです。リレーショナル データベース管理システム (RDBMS) であり、コンマ区切りファイルの単なる SQL ラッパーではありません。

クエリを実行すると、次のことが起こります。

  1. クエリは「パラメータ化された」クエリにコンパイルされ、すべての変数が純粋な構造 SQL に至るまで排除されます。
  2. コンパイル キャッシュがチェックされ、クエリに対して最近使用可能な実行プランが見つかったかどうかが確認されます。
  3. クエリは、必要に応じて実行計画にコンパイルされます (これは「EXPLAIN」が示すものです)
  4. 実行計画要素ごとに、メモリー・キャッシュに最新の使用可能なデータが含まれているかどうかがチェックされます。含まれていない場合、中間データはマスター表データから組み立てられます。
  5. 最終結果は、すべての中間データをまとめることによって組み立てられます。

あなたが見ているのは、クエリのコストが 0.0009 秒のとき、キャッシュはすべてのデータをまとめて提供するのに十分なほど新鮮であり、7.5 秒でピークに達したとき、クエリされたテーブルで何かが変更されたか、他のクエリがインメモリを「プッシュ」したことです。データをキャッシュアウトするか、DBMS に他の理由があり、クエリを再コンパイルするか、すべてのデータを再度フェッチする必要があると考えられます。おそらく、他のバリエーションのいくつかは、使用済みのインデックスがまだメモリに十分に新しくキャッシュされているかどうかに関係しています。

これを結論づけると、クエリは途方もなく遅いです。キャッシングによって高速に見えることが幸運な場合もあります。

これを解決するには、次の 2 つのことを検討することをお勧めします。

  1. 何よりもまず、このサイズのクエリでは、実行計画に "No possible keys" という行が 1 行も含まれていてはなりません。インデックスがどのように機能するかを調査し、結合されたテーブルごとに 1 つのインデックスを使用するという MySQL の制限の影響を認識し、計画の各行が「キー」の下にエントリを持つようにデータベースを微調整します。
  2. 次に、クエリ自体を確認します。DBMS は、生データを結合するだけで最高のパフォーマンスを発揮します。CASEやのようなプログラム要素を使用COALESCEすることは、非常に便利ですが、実行時にデータベースが生のテーブル データを取得するだけでなく、より多くのことを評価する必要があります。このようなステートメントを排除するか、取得したデータの後処理としてビジネス ロジックに移動するようにしてください。

最後に、MySQL は実際にはかなりばかげた DBMS であることを決して忘れないでください。これは、ほとんどの Web サイトで必要とされる単純なデータ フェッチ クエリのパフォーマンス用に最適化されています。そのため、ほとんどの一般的な問題について、SQL Server や Oracle よりもはるかに高速です。関数、ケース、大規模な結合、一致条件などで物事を複雑にし始めると、競合他社は多くの場合、はるかに最適化されており、クエリ コンパイラでより適切に最適化されています。そのため、MySQL が特定のクエリで遅くなり始めたら、混乱しないように 2 つ以上の小さなクエリに分割し、PHP または呼び出す言語で後処理を行うことを検討してください。特にサブクエリが含まれる場合(あなたの場合のように)、MySQLを混乱させないだけで、これによりパフォーマンスが大幅に向上する多くのケースを見てきました。

于 2013-04-17T16:14:23.427 に答える
1

外部クエリと内部クエリの両方が、最小要件 flag_three = 1 AND flag_four = 1 (内部クエリの (( x and y ) または z) 条件に関係なく) を持つ「e」テーブルで動作することから始めましょう。また、外部WHERE 句には a.Flag_two への明示的な参照がありますが、LEFT JOIN を実際に (INNER) JOIN にするように強制する NULL はありません。また、「cat.これは、「ルックアップ」テーブル参照のように見えるので理にかなっています。「m_table」と「c_table」に関しては、それを取得したり何もしたりしていないので、それらは完全に削除されます。

次のクエリで同じ結果が得られるでしょうか?

select 
      e1.id,
      e1.Title,
      e1.Special_Flag,
      e1.cat_id,
      coalesce( a1.date, '9999-99-99' ) ADate,
      coalesce( a1.time, '99-99-99' ) ATime
      cat.LastName
   from
      e_table e1
         LEFT JOIN a_table as a1
             ON e1.id = a1.e_id
            AND a1.flag_two = 1
            AND a1.date >= '2013-03-29'

         JOIN cat_table as cat 
             ON e1.cat_id = cat.id
   where
          e1.flag_three = 1
      and e1.flag_four = 1 
      and (   e1.special_flag = 1
           OR a1.id IS NOT NULL )
   order by
      IF( a1.id is null, 2, 1 ),
      ADate,
      ATime,
      e1.ID Desc
   limit
      0, 10

メインの WHERE 句は、「three and four」フラグが 1 に設定されているものにのみ適用されます (特別なフラグが存在するか、問題の特定の日付以降に有効な「a」レコードが存在する)。

そこから、単純な注文と制限。

日付と時刻の取得に関しては、日付以降のレコードのみを含める必要があるようです。それ以外の場合は無視します(古いもので適用できないなど、表示したくない)。

順番として、「a」ID の NULL 値について FIRST をテストしています。もしそうなら、それらはすべて「9999-99-99」の日付と「99-99-99」の時間に強制され、一番下にプッシュされることを知っています (したがって 2)。 " 記録し、最初にそれらが必要です (したがって 1)。次に、日付/時刻ごとに並べ替え、同じ日付/時刻内に複数ある場合は ID の降順で並べ替えます。

最後に、インデックスを支援するために、「e」テーブルにインデックスがあることを確認します

( id, flag_three, flag_four, special_flag ).

「a」テーブルの場合、インデックス

(e_id, flag_two, date)
于 2013-04-17T19:42:05.260 に答える