MySQL 8.0 リファレンスマニュアル


8.2.1.1 WHERE 句の最適化

このセクションでは、WHERE 句の処理で実行可能な最適化について説明します。 例では SELECT ステートメントを使用していますが、DELETE および UPDATE ステートメント内の WHERE 句にも同じ最適化を適用します。

注記

MySQL オプティマイザへの取り組みは継続中であるため、MySQL が実行する最適化のすべてをここで説明しているわけではありません。

読みやすさを犠牲にしても、算術演算を高速化するように、クエリーを書き換えたいと考えがちです。 MySQL では同様の最適化を自動的に実行するため、多くの場合にこの作業を回避でき、クエリーを理解しやすく、保守しやすい形式のままにしておくことができます。 MySQL によって実行される最適化の一部を次に示します。

  • 不要なかっこの削除:

       ((a AND b) AND c OR (((a AND b) AND (c AND d))))
    -> (a AND b AND c) OR (a AND b AND c AND d)
  • 定数畳み込み:

       (a<b AND b=c) AND a=5
    -> b>5 AND b=c AND a=5
  • 一定条件の削除:

       (b>=5 AND b=5) OR (b=6 AND 5=5) OR (b=7 AND 5=6)
    -> b=5 OR b=6

    MySQL 8.0.14 以降では、これは最適化フェーズではなく準備中に行われるため、結合の簡略化に役立ちます。 詳細および例は、セクション8.2.1.9「外部結合の最適化」 を参照してください。

  • インデックスによって使用される定数式は 1 回だけ評価されます。

  • MySQL 8.0.16 以降では、定数値を持つ数値型のカラムの比較がチェックされて折りたたまれるか、無効な値または範囲外の値がないかどうかが削除されます:

    # CREATE TABLE t (c TINYINT UNSIGNED NOT NULL);
      SELECT * FROM t WHERE c ≪ 256;
    -≫ SELECT * FROM t WHERE 1;

    詳しくはセクション8.2.1.14「定数 - フォールディングの最適化」,をご覧ください。

  • WHERE を使用しない単一テーブルの COUNT(*) は、MyISAM テーブルと MEMORY テーブルのテーブル情報から直接取得されます。 これは、1 つだけのテーブルで使用された場合に、NOT NULL 式にも実行されます。

  • 無効な定数式の早期の検出。 MySQL は一部の SELECT ステートメントが実行不可能であることをすみやかに検出し、行を返しません。

  • GROUP BY または集約関数 (COUNT()MIN() など) を使用しない場合、HAVINGWHERE とマージされます。

  • 結合内の各テーブルについて、テーブルの高速の WHERE 評価を取得し、可能なかぎり早く行をスキップするために、より単純な WHERE が構築されます。

  • クエリー内のほかのすべてのテーブルの前に、まず、すべての定数テーブルが読み取られます。 定数テーブルは次のいずれかです。

    • 空白のテーブルまたは 1 行のテーブル。

    • PRIMARY KEY または UNIQUE インデックスでの WHERE 句で使用されるテーブル。ここではすべてのインデックス部分が定数式と比較され、NOT NULL として定義されます。

    次のテーブルはすべて定数テーブルとして使用されます。

    SELECT * FROM t WHERE primary_key=1;
    SELECT * FROM t1,t2
      WHERE t1.primary_key=1 AND t2.primary_key=t1.id;
  • テーブルを結合するための最適な結合の組み合わせは、すべての可能性を試してみることで見つかります。 ORDER BY および GROUP BY 句内のすべてのカラムが同じテーブルにある場合、結合する際に最初にそのテーブルが選ばれます。

  • ORDER BY 句と別の GROUP BY 句がある場合、または、ORDER BY または GROUP BY に結合キュー内の最初のテーブルと異なるテーブルのカラムが含まれている場合は、一時テーブルが作成されます。

  • SQL_SMALL_RESULT 修飾子を使用する場合、MySQL はインメモリー一時テーブルを使用します。

  • オプティマイザがテーブルスキャンを使用する方が効率的であると判断しないかぎり、各テーブルインデックスがクエリーされ、最適なインデックスが使用されます。 かつて、スキャンは、最適なインデックスがテーブルの 30% 超にまたがっているかどうかに基づいて使用されていましたが、固定のパーセンテージによって、インデックスを使用するか、スキャンを使用するかの選択が決定されなくなりました。 現在のオプティマイザは複雑になり、テーブルサイズ、行数、I/O ブロックサイズなどの追加の要因に基づいて推定します。

  • 場合によって、MySQL はデータファイルを参照しなくてもインデックスから行を読み取ることができます。 インデックスから使用されるすべてのカラムが数値の場合、クエリーの解決にインデックスツリーのみが使用されます。

  • 各行が出力される前に、HAVING 句に一致しないものはスキップされます。

きわめて高速なクエリーのいくつかの例:

SELECT COUNT(*) FROM tbl_name;

SELECT MIN(key_part1),MAX(key_part1) FROM tbl_name;

SELECT MAX(key_part2) FROM tbl_name
  WHERE key_part1=constant;

SELECT ... FROM tbl_name
  ORDER BY key_part1,key_part2,... LIMIT 10;

SELECT ... FROM tbl_name
  ORDER BY key_part1 DESC, key_part2 DESC, ... LIMIT 10;

MySQL は、インデックス設定されたカラムが数値であるとして、インデックスツリーのみを使用して、次のクエリーを解決します。

SELECT key_part1,key_part2 FROM tbl_name WHERE key_part1=val;

SELECT COUNT(*) FROM tbl_name
  WHERE key_part1=val1 AND key_part2=val2;

SELECT key_part2 FROM tbl_name GROUP BY key_part1;

次のクエリーは、個別のソーティングパスを使用せずに、インデックスを使用して、ソート順で行を取得します。

SELECT ... FROM tbl_name
  ORDER BY key_part1,key_part2,... ;

SELECT ... FROM tbl_name
  ORDER BY key_part1 DESC, key_part2 DESC, ... ;

関連キーワード:  テーブル, インデックス, key, WHERE, InnoDB, 結合, クエリー, FROM, ステートメント, 定数