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


MySQL 8.0 リファレンスマニュアル  /  ...  /  EXPLAIN 出力フォーマット

8.8.2 EXPLAIN 出力フォーマット

EXPLAIN ステートメントは、MySQL がステートメントを実行する方法に関する情報を提供します。 EXPLAIN は、SELECT, DELETE, INSERT, REPLACE および UPDATE ステートメントで動作します。

EXPLAINSELECT ステートメントで使用される各テーブルに関する情報の行を返します。 これは、MySQL がステートメントの処理中にテーブルを読み取る順番で、出力にテーブルを一覧表示します。 これは、MySQL が最初のテーブルから行を読み取り、次に 2 番目のテーブル、3 番目のテーブルなどで一致する行を検索することを意味します。 すべてのテーブルが処理されると、MySQL は選択したカラムを出力し、さらに一致する行があるテーブルが見つかるまで、テーブルリストを逆戻りします。 次の行がテーブルから読み取られ、プロセスは次のテーブルに進みます。

注記

MySQL Workbench には、EXPLAIN 出力を視覚的に表現する Visual Explain 機能があります。 Tutorial: Using Explain to Improve Query Performanceを参照してください。

EXPLAIN 出力カラム

このセクションでは、EXPLAIN によって生成される出力カラムについて説明します。 あとのセクションで、typeExtra カラムに関する追加情報を提供します。

EXPLAIN からの各出力行は 1 つのテーブルに関する情報を提供します。 各行には、表8.1「EXPLAIN 出力カラム」で要約し、次の表に詳しく説明している値が格納されます。 テーブルの最初のカラムにはカラム名が表示されます。2 番目のカラムには、FORMAT=JSON を使用した場合の出力に表示される同等のプロパティ名が示されます。

表 8.1 EXPLAIN 出力カラム

カラム JSON 名 意味
id select_id SELECT 識別子。
select_type なし SELECT
table table_name 出力行のテーブル
partitions partitions 一致するパーティション
type access_type 結合型
possible_keys possible_keys 選択可能なインデックス
key key 実際に選択されたインデックス
key_len key_length 選択されたキーの長さ
ref ref インデックスと比較されるカラム
rows rows 調査される行の見積もり
filtered filtered テーブル条件によってフィルタ処理される行の割合
Extra なし 追加情報

注記

NULL である JSON プロパティは、JSON 形式の EXPLAIN 出力には表示されません。

  • id (JSON 名): select_id )

    SELECT 識別子。 これはクエリー内の SELECT の連番です。 行がほかの行の和集合結果を参照する場合に、値は NULL になることがあります。 この場合、table カラムには、<unionM,N> などの値が表示され、行が M および Nid 値のある行の和集合を参照していることが示されます。

  • select_type (JSON 名): none)

    SELECT の種類で、次の表に示すもののいずれかになります。 JSON 形式の EXPLAIN は、SIMPLE または PRIMARY でないかぎり、SELECT タイプを query_block のプロパティとして公開します。 JSON 名 (該当する場合) もテーブルに示されます。

    select_type JSON 名 意味
    SIMPLE なし 単純な SELECT (UNION やサブクエリーを使用しません)
    PRIMARY なし もっとも外側の SELECT
    UNION なし UNION 内の 2 つめ以降の SELECT ステートメント
    DEPENDENT UNION dependent (true) UNION 内の 2 つめ以降の SELECT ステートメントで、外側のクエリーに依存します
    UNION RESULT union_result UNION の結果。
    SUBQUERY なし サブクエリー内の最初の SELECT
    DEPENDENT SUBQUERY dependent (true) サブクエリー内の最初の SELECT で、外側のクエリーに依存します
    DERIVED なし 導出テーブル
    DEPENDENT DERIVED dependent (true) 別のテーブルに依存する導出テーブル
    MATERIALIZED materialized_from_subquery 実体化されたサブクエリー
    UNCACHEABLE SUBQUERY cacheable (false) 結果をキャッシュできず、外側のクエリーの行ごとに再評価される必要があるサブクエリー
    UNCACHEABLE UNION cacheable (false) キャッシュ不可能なサブクエリー (UNCACHEABLE SUBQUERY を参照してください) に属する UNION 内の 2 つめ以降の SELECT

    DEPENDENT は一般に、相関サブクエリーの使用を示します。 セクション13.2.11.7「相関サブクエリー」を参照してください。

    DEPENDENT SUBQUERY の評価は UNCACHEABLE SUBQUERY の評価とは異なります。 DEPENDENT SUBQUERY の場合、その外部コンテキストの変数の異なる値の各セットにつき、一回だけサブクエリーが再評価されます。 UNCACHEABLE SUBQUERY の場合、外部コンテキストの行ごとにサブクエリーが再評価されます。

    EXPLAINFORMAT=JSON を指定した場合、出力には select_type と直接同等の単一のプロパティはありません。query_block プロパティは特定の SELECT に対応します。 表示されているほとんどの SELECT サブクエリータイプに相当するプロパティが使用可能で (たとえば、materialized_from_subquery for MATERIALIZED)、必要に応じて表示されます。 SIMPLE または PRIMARY に相当する JSON はありません。

    SELECT 以外のステートメントの select_type 値には、影響を受けるテーブルのステートメントタイプが表示されます。 たとえば、select_typeDELETE ステートメント用の DELETE です。

  • table (JSON 名): table_name )

    出力の行で参照しているテーブルの名前。 これも次のいずれかの値になることがあります。

    • <unionM,N>: 行は M および Nid 値のある行の和集合を参照しています。

    • <derivedN>: 行は Nid 値のある行の派生テーブル結果を参照しています。 派生テーブルは、たとえば FROM 句内のサブクエリーの結果などになります。

    • <subqueryN>: 行は Nid 値のある行の実体化されたサブクエリーの結果を参照しています。 セクション8.2.2.2「実体化を使用したサブクエリーの最適化」を参照してください。

  • partitions (JSON 名): partitions )

    クエリーでレコードが照合されるパーティション。 パーティション化されていないテーブルの場合、この値は NULL です。 セクション24.3.5「パーティションに関する情報を取得する」を参照してください。

  • type (JSON 名): access_type )

    結合型。 さまざまな型の説明については、「EXPLAIN 結合型」を参照してください。

  • possible_keys (JSON 名): possible_keys )

    possible_keys カラムは、MySQL がこのテーブルの行を検索するために選択できるインデックスを示します。 このカラムは EXPLAIN の出力に表示されたテーブルの順序にまったく依存しません。 つまり、possible_keys のキーの一部は、生成されたテーブルの順序で実際に使用できないことがあります。

    このカラムが NULL の場合 (または JSON 形式の出力で未定義の場合)、関連するインデックスはありません。 この場合、WHERE 句を調査して、それがインデックス設定に適したカラムを参照しているかどうかをチェックすることで、クエリーのパフォーマンスを向上できることがあります。 その場合は、適切なインデックスを作成し、再度 EXPLAIN でクエリーをチェックします。 セクション13.1.9「ALTER TABLE ステートメント」を参照してください。

    テーブルにあるインデックスを確認するには、SHOW INDEX FROM tbl_name を使用します。

  • key (JSON 名): key )

    key カラムは、MySQL が実際に使用することを決定したキー (インデックス) を示します。 MySQL が行をルックアップするために、いずれかの possible_keys インデックスを使用することを決定した場合、キー値としてそのインデックスが一覧表示されます。

    key は、possible_keys 値に存在しないインデックスに名前を付けることができます。 これは possible_keys インデックスのどれも行のルックアップに適していない場合に発生する可能性がありますが、クエリーによって選択されるすべてのカラムはほかのインデックスのカラムになります。 つまり、指定されたインデックスは選択されたカラムをカバーするため、取得する行を決定するために使用されませんが、インデックススキャンはデータ行スキャンよりも効率的です。

    InnoDB は各セカンダリインデックスとともに主キー値を保存するため、InnoDB では、クエリーで主キーも選択している場合でも、セカンダリインデックスで選択されたカラムをカバーしている可能性があります。 keyNULL の場合、MySQL はクエリーをより効率的に実行するために使用するインデックスを見つけられませんでした。

    MySQL で possible_keys カラムに示されたインデックスを強制的に使用させるか、無視させるには、クエリーで FORCE INDEXUSE INDEX、または IGNORE INDEX を使用します。 セクション8.9.4「インデックスヒント」を参照してください。

    MyISAM テーブルの場合、ANALYZE TABLE を実行すると、オプティマイザがより適切なインデックスを選択するのに役立ちます。 MyISAM テーブルの場合、myisamchk --analyze も同様に動作します。 セクション13.7.3.1「ANALYZE TABLE ステートメント」およびセクション7.6「MyISAM テーブルの保守とクラッシュリカバリ」を参照してください。

  • key_len (JSON 名): key_length )

    key_len カラムは、MySQL が使用することを決定したキーの長さを示します。 key_len の値を使用すると、MySQL が実際に使用するマルチパーティキーの部分の数を決定できます。 key カラムに NULL と表示されている場合、key_len カラムにも NULL と表示されます。

    キーの格納形式のため、キーの長さは、NULL にできるカラムの長さが NOT NULL カラムの長さより大きくなります。

  • ref (JSON 名): ref )

    ref カラムは、テーブルから行を選択するために、key カラムに指定されたインデックスに対して比較されるカラムまたは定数を示します。

    値が func の場合、使用される値は、特定の関数の結果です。 どの関数を表示するには、EXPLAIN の後の SHOW WARNINGS を使用して、拡張 EXPLAIN 出力を表示します。 関数は、実際には算術演算子などの演算子である場合があります。

  • rows (JSON 名): rows )

    rows カラムは、MySQL がクエリーを実行するために調査する必要があると考える行数を示します。

    InnoDB テーブルの場合、これは推定値であり、常に正確ではないことがあります。

  • filtered (JSON 名): filtered )

    filtered カラムは、テーブル条件でフィルタされるテーブルの行の推定割合を示します。 最大値は 100 で、これは行のフィルタリングが行われなかったことを意味します。 100 から減少する値は、フィルタリングの量が増加していることを示します。rows には調査された推定行数が表示され、rows×filtered には次のテーブルと結合された行数が表示されます。 たとえば、rows が 1000 で filtered が 50.00 (50%) の場合、次のテーブルと結合される行数は 1000×50% = 500 になります。

  • Extra (JSON 名): none)

    このカラムには、MySQL がクエリーを解決する方法に関する追加情報が含まれます。 さまざまな値の説明については、「EXPLAIN の追加情報」を参照してください。

    Extra カラムに対応する単一の JSON プロパティはありませんが、このカラムで発生する可能性のある値は JSON プロパティまたは message プロパティのテキストとして公開されます。

EXPLAIN 結合型

EXPLAIN 出力の type カラムには、テーブルの結合方法が示されます。 JSON 形式の出力では、これらは access_type プロパティの値として検出されます。 次のリストに、もっとも適切な型からもっとも不適切な型の順番で並べた結合型を示します。

  • system

    テーブルには行が 1 つしかありません (= system テーブル)。 これは、const 結合型の特殊なケースです。

  • const

    テーブルには、一致するレコードが最大で 1 つあり、クエリーの開始時に読み取られます。 行が 1 つしかないため、この行のカラムの値は、オプティマイザの残りによって定数とみなされることがあります。const テーブルは、1 回しか読み取られないため、非常に高速です。

    constPRIMARY KEY または UNIQUE インデックスのすべてのパートを定数値と比較する場合に使用されます。 次のクエリーでは、tbl_nameconst テーブルとして使用できます。

    SELECT * FROM tbl_name WHERE primary_key=1;
    
    SELECT * FROM tbl_name
      WHERE primary_key_part1=1 AND primary_key_part2=2;
  • eq_ref

    前のテーブルの行の組み合わせごとに、このテーブルから 1 行ずつ読み取られます。 systemconst 型以外で、これは最適な結合型です。 これは、結合でインデックスのすべてのパートが使用されており、インデックスが PRIMARY KEY または UNIQUE NOT NULL インデックスである場合に使用されます。

    eq_ref は、= 演算子を使用して比較されるインデックス設定されたカラムに使用できます。 比較値は、定数またはこのテーブルより前に読み取られたテーブルのカラムを使用する式を指定できます。 次の例では、MySQL は eq_ref 結合を使用して、ref_table を処理できます。

    SELECT * FROM ref_table,other_table
      WHERE ref_table.key_column=other_table.column;
    
    SELECT * FROM ref_table,other_table
      WHERE ref_table.key_column_part1=other_table.column
      AND ref_table.key_column_part2=1;
  • ref

    前のテーブルの行の組み合わせごとに、一致するインデックス値を持つすべての行がこのテーブルから読み取られます。ref は、結合でキーの左端のプリフィクスのみが使用される場合、またはキーが PRIMARY KEYUNIQUE インデックスではない場合 (つまり、結合で、キー値に基づいて単一の行を選択できない場合) に使用されます。 使用されているキーがほんの数行にしか一致しない場合、これは適切な結合型です。

    ref は、= または <=> 演算子を使用して比較されるインデックス設定されたカラムに使用できます。 次の例では、MySQL は ref 結合を使用して、ref_table を処理できます。

    SELECT * FROM ref_table WHERE key_column=expr;
    
    SELECT * FROM ref_table,other_table
      WHERE ref_table.key_column=other_table.column;
    
    SELECT * FROM ref_table,other_table
      WHERE ref_table.key_column_part1=other_table.column
      AND ref_table.key_column_part2=1;
  • fulltext

    結合は FULLTEXT インデックスを使用して実行されます。

  • ref_or_null

    この結合型は、ref と似ていますが、MySQL が NULL 値を含む行の追加検索を実行することが追加されます。 この結合型の最適化は、ほとんどの場合に、サブクエリーの解決で使用されます。 次の例では、MySQL は ref_or_null 結合を使用して、ref_table を処理できます。

    SELECT * FROM ref_table
      WHERE key_column=expr OR key_column IS NULL;

    セクション8.2.1.15「IS NULL の最適化」を参照してください。

  • index_merge

    この結合型はインデックスマージ最適化が使用されたことを示します。 この場合、出力行の key カラムには使用されたインデックスのリストが含まれ、key_len には使用されたインデックスの最長キーパートのリストが含まれます。 詳細については、セクション8.2.1.3「インデックスマージの最適化」を参照してください。

  • unique_subquery

    このタイプは、次の形式の一部の IN サブクエリーで eq_ref に置き換わります:

    value IN (SELECT primary_key FROM single_table WHERE some_expr)

    unique_subquery は、効率化のため、サブクエリーを完全に置き換える単なるインデックスルックアップ関数です。

  • index_subquery

    この結合型は unique_subquery に似ています。 IN サブクエリーを置き換えますが、次の形式のサブクエリー内の一意でないインデックスに対して機能します。

    value IN (SELECT key_column FROM single_table WHERE some_expr)
  • range

    行を選択するためのインデックスを使用して、特定の範囲にある行のみが取得されます。 出力行の key カラムは、使用されるインデックスを示します。 key_len には使用された最長のインデックスパートが格納されます。 この型の ref カラムは NULL です。

    range は、=, <>, >, >=, <, <=, IS NULL, <=>, BETWEEN, LIKE または IN() 演算子のいずれかを使用してキーカラムを定数と比較する場合に使用できます:

    SELECT * FROM tbl_name
      WHERE key_column = 10;
    
    SELECT * FROM tbl_name
      WHERE key_column BETWEEN 10 and 20;
    
    SELECT * FROM tbl_name
      WHERE key_column IN (10,20,30);
    
    SELECT * FROM tbl_name
      WHERE key_part1 = 10 AND key_part2 IN (10,20,30);
  • インデックス

    index 結合型は、インデックスツリーがスキャンされることを除いて、ALL と同じです。 これは 2 つの方法で行われます。

    • インデックスがクエリーのカバリングインデックスで、使用すると、テーブルから必要なすべてのデータを満たすことができる場合、インデックスツリーのみがスキャンされます。 この場合、Extra カラムには Using index と示されます。 インデックスのサイズは通常テーブルデータより小さいため、インデックスのみのスキャンは通常、ALL より高速です。

    • フルテーブルスキャンは、インデックスからの読み取りを使用して、インデックス順でデータ行をルックアップして実行されます。 Extra カラムに Uses index が表示されません。

    MySQL は、クエリーで単一のインデックスの一部であるカラムのみが使用されている場合に、この結合型を使用できます。

  • ALL

    フルテーブルスキャンは、前のテーブルの行の組み合わせごとに実行されます。 これは、通常テーブルが const とマークされていない最初のテーブルである場合には適しておらず、通常ほかのすべてのケースで著しく不適切です。 通常、定数値または以前のテーブルからのカラム値に基づいて、テーブルからの行の取得を可能にするインデックスを追加することで、ALL を回避できます。

EXPLAIN 追加情報

EXPLAIN 出力の Extra カラムには、MySQL がクエリーを解決する方法に関する追加情報が含まれます。 次のリストに、このカラムに表示される可能性のある値について説明します。 各セクション目は、JSON 形式の出力に対して、Extra 値を表示するプロパティも示します。 これらの一部には、特定のプロパティがあります。 その他は、message プロパティのテキストとして表示されます。

クエリーをできるだけ高速にする場合は、Using filesort および Using temporaryExtra カラムの値を検索するか、JSON 形式の EXPLAIN 出力で using_filesort および using_temporary_table のプロパティが true に等しいかどうかを調べます。

  • Child of 'table' pushed join@1 (JSON: message テキスト)

    このテーブルは、NDB カーネルにプッシュダウンできる結合内の table の子として参照されます。 プッシュダウン結合が有効になっている場合、NDB Cluster でのみ適用されます。 詳細と例については、ndb_join_pushdown サーバーシステム変数の説明を参照してください。

  • const row not found (JSON プロパティ): const_row_not_found )

    SELECT ... FROM tbl_name などのクエリーの場合、テーブルは空でした。

  • Deleting all rows (JSON プロパティ): message )

    DELETE に対し、一部のストレージエンジン (MyISAM など) は簡単で高速にすべての行テーブルを削除するハンドラメソッドをサポートしています。 この Extra 値は、エンジンでこの最適化が使用された場合に表示されます。

  • Distinct (JSON プロパティ): distinct )

    MySQL は個別の値を検索するため、最初に一致する行が見つかったら、現在の行の組み合わせについてのそれ以上の行の検索を停止します。

  • FirstMatch(tbl_name) (JSON プロパティ): first_match )

    準結合 FirstMatch 結合ショートカット戦略は、tbl_name に使用されます。

  • Full scan on NULL key (JSON プロパティ): message )

    これは、オプティマイザがインデックスルックアップアクセスメソッドを使用できない場合の代替の戦略として、サブクエリーの最適化で行われます。

  • Impossible HAVING (JSON プロパティ): message )

    HAVING 句は常に false で、どの行も選択できません。

  • Impossible WHERE (JSON プロパティ): message )

    WHERE 句は常に false で、どの行も選択できません。

  • Impossible WHERE noticed after reading const tables (JSON プロパティ): message )

    MySQL はすべての const (および system) テーブルを読み取り、WHERE 句が常に false であることを通知します。

  • LooseScan(m..n) (JSON プロパティ): message )

    準結合 LooseScan 戦略が使用されます。m および n は主要な部品番号です。

  • No matching min/max row (JSON プロパティ): message )

    SELECT MIN(...) FROM ... WHERE condition などのクエリーの条件を満たす行がありません。

  • no matching row in const table (JSON プロパティ): message )

    結合のあるクエリーで、空のテーブルまたは一意のインデックス条件を満足する行がないテーブルがありました。

  • No matching rows after partition pruning (JSON プロパティ): message )

    DELETE または UPDATE に対し、オプティマイザはパーティションのプルーニング後に削除または更新するものが何も見つかりませんでした。 それは、SELECT ステートメントの Impossible WHERE に意味が似ています。

  • No tables used (JSON プロパティ): message )

    クエリーに FROM 句がないか、FROM DUAL 句があります。

    INSERT または REPLACE ステートメントで、SELECT パートがない場合に、EXPLAIN にこの値が表示されます。 たとえば、EXPLAIN INSERT INTO t VALUES(10) に対して、それは EXPLAIN INSERT INTO t SELECT 10 FROM DUAL と同等であるために表示されます。

  • Not exists (JSON プロパティ): message )

    MySQL はクエリーに対する LEFT JOIN 最適化を実行でき、LEFT JOIN 条件に一致する 1 つの行が見つかったら、前の行の組み合わせについて、このテーブルでそれ以上の行を調査しません。 これは、このように最適化できるクエリーの種類の例です。

    SELECT * FROM t1 LEFT JOIN t2 ON t1.id=t2.id
      WHERE t2.id IS NULL;

    t2.idNOT NULL で定義されているとします。 この場合、MySQL は t1 をスキャンし、t1.id の値を使用して t2 内の行をルックアップします。 MySQL が t2 内に一致する行を見つけた場合、t2.idNULL にならないことがわかっているため、同じ id 値を持つ t2 内の残りの行をスキャンしません。 つまり、t1 の各行について、MySQL は、t2 内の実際に一致する行数にかかわらず、t2 内の単一のルックアップのみを実行する必要があります。

    MySQL 8.0.17 以降では、NOT IN (subquery) または NOT EXISTS (subquery) 形式の WHERE 条件が内部的にアンチ結合に変換されたことを示すこともできます。 これにより、サブクエリーが削除され、そのテーブルが最上位のクエリーの計画に追加され、コスト計画が改善されます。 準結合とアンチ結合をマージすることで、オプティマイザは実行計画内のテーブルの順序をより自由に変更できるため、計画が高速になる場合があります。

    特定のクエリーに対してアンチ結合変換が実行されるタイミングを確認するには、EXPLAIN の実行後に SHOW WARNINGS から Message カラムを確認するか、EXPLAIN FORMAT=TREE の出力で確認します。

    注記

    アンチ結合は、準結合 table_a JOIN table_b ON condition を補完したものです。 アンチ結合では、condition に一致する行が table_b にない table_a のすべての行が返されます。

  • Plan isn't ready yet (JSON プロパティ): none)

    この値は、オプティマイザが名前付き接続で実行中のステートメントの実行計画の作成を終了していない場合に、EXPLAIN FOR CONNECTION で発生します。 実行計画の出力が複数の行で構成されている場合、オプティマイザが完全な実行計画を決定する進行状況に応じて、そのいずれかまたはすべてがこの Extra 値を持つ可能性があります。

  • Range checked for each record (index map: N) (JSON プロパティ) : message )

    MySQL は使用に適したインデックスを見つけられませんでしたが、前のテーブルからのカラム値がわかったあとに、いくつかのインデックスが使用できることがわかりました。 以前のテーブルの行の組み合わせごとに、MySQL は range または index_merge アクセスメソッドを使用して、行を取得できるかどうかをチェックします。 これは、非常に高速ではありませんが、インデックスがまったくない結合の実行より高速です。 前のテーブルのすべてのカラム値がわかっており、定数とみなされることを除き、適用基準は、セクション8.2.1.2「range の最適化」セクション8.2.1.3「インデックスマージの最適化」で説明されているとおりです。

    インデックスは、テーブルの SHOW INDEX に示される同じ順序で 1 から番号付けされます。 インデックスマップ値 N は、候補となるインデックスを示すビットマスク値です。 たとえば、0x19 (バイナリ 11001) の値は、インデックス 1、4、および 5 が考慮されることを意味します。

  • Recursive (JSON プロパティ): recursive )

    これは、行が再帰的共通テーブル式の再帰的 SELECT 部分に適用されることを示します。 セクション13.2.15「WITH (共通テーブル式)」を参照してください。

  • Rematerialize (JSON プロパティ): rematerialize )

    Rematerialize (X,...) は、T テーブルの EXPLAIN 行に表示されます。X は、T の新しい行が読み取られたときに再実体化がトリガーされるラテラル導出テーブルです。 例:

    SELECT
      ...
    FROM
      t,
      LATERAL (derived table that refers to t) AS dt
    ...

    導出テーブルの内容は、上位クエリーによって t の新しい行が処理されるたびに最新になるように再実体化されます。

  • Scanned N databases (JSON プロパティ): message )

    これは、セクション8.2.3「INFORMATION_SCHEMA クエリーの最適化」に説明するように、サーバーが INFORMATION_SCHEMA テーブルのクエリーを処理する際に実行するディレクトリスキャンの数を示します。 N の値は 0、1、または all です。

  • Select tables optimized away (JSON プロパティ): message )

    オプティマイザは、1) 最大 1 つの行を戻す必要があると判断しました。2) この行を生成するには、確定的な行セットを読み取る必要があります。 読取り対象の行を最適化フェーズ中 (インデックス行の読取りなど) に読み取ることができる場合、クエリーの実行中にテーブルを読み取る必要はありません。

    最初の条件は、クエリーが暗黙的にグループ化されるときに満たされます (集計関数は含まれますが、GROUP BY 句は含まれません)。 2 番目の条件は、使用されるインデックスごとに 1 つの行検索が実行されるときに満たされます。 読み取られるインデックスの数によって、読み取る行数が決まります。

    暗黙的にグループ化された次のクエリーについて考えてみます:

    SELECT MIN(c1), MIN(c2) FROM t1;

    あるインデックス行を読み取ることで MIN(c1) を取得でき、別のインデックスからある行を読み取ることで MIN(c2) を取得できるとします。 つまり、カラム c1 および c2 ごとに、カラムがインデックスの最初のカラムであるインデックスが存在します。 この場合、2 つの決定的な行を読み取ることによって生成された 1 つの行が返されます。

    読み取る行が決定的でない場合、この Extra 値は発生しません。 次のクエリーについて考えてみます:

    SELECT MIN(c2) FROM t1 WHERE c1 <= 10;

    (c1, c2) がカバーインデックスであるとします。 このインデックスを使用して、c1 <= 10 のすべての行をスキャンし、c2 の最小値を検索する必要があります。 対照的に、次のクエリーについて考えてみます:

    SELECT MIN(c2) FROM t1 WHERE c1 = 10;

    この場合、c1 = 10 の最初のインデックス行には、c2 の最小値が含まれます。 返される行を生成するには、1 つの行のみを読み取る必要があります。

    テーブルごとに正確な行数を保持するストレージエンジン (MyISAM など、InnoDB は保持しない) の場合、WHERE 句が欠落しているか常に true で、GROUP BY 句がない COUNT(*) クエリーに対してこの Extra 値が発生することがあります。 (これは暗黙的にグループ化されたクエリーのインスタンスであり、ストレージエンジンは確定的な行数を読み取ることができるかどうかに影響します。)

  • Skip_open_table, Open_frm_only, Open_full_table (JSON プロパティ): message )

    これらの値は、INFORMATION_SCHEMA テーブルのクエリーに適用されるファイルオープンの最適化を示します。

    • Skip_open_table: テーブルファイルを開く必要はありません。 この情報はデータディクショナリからすでに使用可能です。

    • Open_frm_only: テーブル情報を読み取る必要があるのはデータディクショナリのみです。

    • Open_full_table: 最適化されていない情報参照。 テーブル情報は、データディクショナリから、およびテーブルファイルを読み取ることによって読み取る必要があります。

  • Start temporaryEnd temporary (JSON プロパティ): message )

    これは、準結合重複除去ストラテジの一時テーブルの使用を示します。

  • unique row not found (JSON プロパティ): message )

    SELECT ... FROM tbl_name などのクエリーの場合に、テーブルに UNIQUE インデックスまたは PRIMARY KEY の条件を満たす行がありません。

  • Using filesort (JSON プロパティ): using_filesort )

    MySQL はソート順で行を取得する方法を見つけるために、追加のパスを実行する必要があります。 ソートは、結合型に従ってすべての行を進み、ソートキーと WHERE 句に一致するすべての行について行へのポインタを格納して実行されます。 次にキーがソートされ、ソート順で行が取得されます。 セクション8.2.1.16「ORDER BY の最適化」を参照してください。

  • Using index (JSON プロパティ): using_index )

    実際の行を読み取るための追加のシークを実行する必要がなく、インデックスツリーの情報のみを使用して、テーブルからカラム情報が取得されます。 この戦略は、クエリーで単一のインデックスの一部であるカラムのみを使用している場合に使用できます。

    ユーザー定義のクラスタ化されたインデックスを持つ InnoDB テーブルの場合、そのインデックスは Extra カラムに Using index がない場合でも使用できます。 これは、typeindexkeyPRIMARY の場合です。

  • Using index condition (JSON プロパティ): using_index_condition )

    インデックスタプルにアクセスし、まずそれらをテストして、すべてのテーブル行を読み取るかどうかを判断することによって、テーブルが読み取られます。 このように、必要でないかぎり、すべてのテーブル行の読み取りを遅延 (プッシュダウン) するためにインデックス情報が使用されます。 セクション8.2.1.6「インデックスコンディションプッシュダウンの最適化」を参照してください。

  • Using index for group-by (JSON プロパティ): using_index_for_group_by )

    Using index テーブルアクセスメソッドと同様に、Using index for group-by は MySQL が、実際のテーブルへの追加のディスクアクセスをせずに、GROUP BY または DISTINCT クエリーのすべてのカラムを取得するために使用できるインデックスを見つけたことを示します。 さらに、各グループに対して、少数のインデックスエントリだけが読み取られるように、インデックスがもっとも効率的に使われます。 詳細は、セクション8.2.1.17「GROUP BY の最適化」を参照してください。

  • Using index for skip scan (JSON プロパティ): using_index_for_skip_scan )

    スキップスキャンアクセスメソッドが使用されていることを示します。 スキャン範囲アクセス方法のスキップを参照してください。

  • Using join buffer (Block Nested Loop), Using join buffer (Batched Key Access), Using join buffer (hash join) (JSON プロパティ): using_join_buffer )

    初期の結合からのテーブルは、部分ごとに結合バッファーに読み込まれ、それらの行がバッファーから使用されて、現在のテーブルとの結合が実行されます。 (Block Nested Loop) ではブロックネスト - ループアルゴリズムが使用され、(Batched Key Access) ではバッチキーアクセスアルゴリズムが使用され、(hash join) ではハッシュ結合が使用されます。 つまり、EXPLAIN 出力の前の行にあるテーブルのキーがバッファされ、Using join buffer が表示される行で表されるテーブルから一致する行がバッチでフェッチされます。

    JSON 形式の出力では、using_join_buffer の値は常に Block Nested LoopBatched Key Access または hash join のいずれかです。

    ハッシュ結合は、MySQL 8.0.18 以降で使用できます。Block Nested-Loop アルゴリズムは、MySQL 8.0.20 以降の MySQL リリースでは使用されません。 これらの最適化の詳細は、セクション8.2.1.4「ハッシュ結合の最適化」 および Block Nested Loop 結合アルゴリズム を参照してください。

    バッチキーアクセスアルゴリズムの詳細は、Batched Key Access 結合 を参照してください。

  • Using MRR (JSON プロパティ): message )

    テーブルは Multi-Range Read 最適化戦略を使用して読み取られます。 セクション8.2.1.11「Multi-Range Read の最適化」を参照してください。

  • Using sort_union(...), Using union(...), Using intersect(...) (JSON プロパティ): message )

    これらは、index_merge 結合タイプのインデックススキャンのマージ方法を示す特定のアルゴリズムを示します。 セクション8.2.1.3「インデックスマージの最適化」を参照してください。

  • Using temporary (JSON プロパティ): using_temporary_table )

    クエリーを解決するために、MySQL は結果を保持する一時テーブルを作成する必要があります。 これは一般に、クエリーに、カラムを異なって一覧表示する GROUP BY 句と ORDER BY 句が含まれる場合に発生します。

  • Using where (JSON プロパティ): attached_condition )

    WHERE 句は、次のテーブルに対して照合されるか、またはクライアントに送信される行を制限するために使用されます。 具体的にテーブルからすべての行をフェッチするか、調査する意図がないかぎり、Extra 値が Using where でなく、テーブル結合型が ALL または index である場合、クエリーに何らかの誤りがある可能性があります。

    JSON 形式の出力では、Using where に直接対応するものはありません。attached_condition プロパティには、使用される WHERE 条件が含まれます。

  • Using where with pushed condition (JSON プロパティ): message )

    この項目は NDB テーブルのみに適用されます。 つまり、NDB Cluster は条件プッシュダウン最適化を使用して、インデックスなしカラムと定数の間の直接比較の効率を向上させています。 そのような場合、条件がクラスタのデータノードにプッシュダウンされ、すべてのデータノードで同時に評価されます。 これにより、一致しない行をネットワーク経由で送る必要がなくなり、コンディションプッシュダウンを使用できるが使用しない場合より、そのようなクエリーを 5 - 10 倍高速化できます。 詳細は、セクション8.2.1.5「エンジンコンディションプッシュダウンの最適化」を参照してください。

  • Zero limit (JSON プロパティ): message )

    クエリーに LIMIT 0 句があり、行を選択できません。

EXPLAIN 出力の解釈

EXPLAIN 出力の rows カラムの値の積を取得することで、結合がどの程度適しているかを示す適切な目安を得ることができます。 これは、クエリーを実行するために MySQL が調査する必要がある行数を大ざっぱに示すはずです。 max_join_size システム変数によってクエリーを制限する場合、この行の積は、どの複数テーブル SELECT ステートメントを実行し、どれを中止するかを判断するためにも使用されます。 セクション5.1.1「サーバーの構成」を参照してください。

次の例は、EXPLAIN によって得られた情報に基づいて、複数テーブル結合を段階的に最適化する方法を示しています。

ここに示す SELECT ステートメントがあり、EXPLAIN を使用して調査するつもりであるとします。

EXPLAIN SELECT tt.TicketNumber, tt.TimeIn,
               tt.ProjectReference, tt.EstimatedShipDate,
               tt.ActualShipDate, tt.ClientID,
               tt.ServiceCodes, tt.RepetitiveID,
               tt.CurrentProcess, tt.CurrentDPPerson,
               tt.RecordVolume, tt.DPPrinted, et.COUNTRY,
               et_1.COUNTRY, do.CUSTNAME
        FROM tt, et, et AS et_1, do
        WHERE tt.SubmitTime IS NULL
          AND tt.ActualPC = et.EMPLOYID
          AND tt.AssignedPC = et_1.EMPLOYID
          AND tt.ClientID = do.CUSTNMBR;

この例では次のように想定しています。

  • 比較対象のカラムは次のように宣言されています。

    Table カラム データ型
    tt ActualPC CHAR(10)
    tt AssignedPC CHAR(10)
    tt ClientID CHAR(10)
    et EMPLOYID CHAR(15)
    do CUSTNMBR CHAR(15)
  • テーブルには次のインデックスがあります。

    Table インデックス
    tt ActualPC
    tt AssignedPC
    tt ClientID
    et EMPLOYID (主キー)
    do CUSTNMBR (主キー)
  • tt.ActualPC 値は均一に分布されていません。

最初、最適化が実行される前は、EXPLAIN ステートメントで次の情報が生成されました。

table type possible_keys key  key_len ref  rows  Extra
et    ALL  PRIMARY       NULL NULL    NULL 74
do    ALL  PRIMARY       NULL NULL    NULL 2135
et_1  ALL  PRIMARY       NULL NULL    NULL 74
tt    ALL  AssignedPC,   NULL NULL    NULL 3872
           ClientID,
           ActualPC
      Range checked for each record (index map: 0x23)

各テーブルの typeALL であるため、この出力は MySQL がすべてのテーブル、つまりすべての行の組み合わせのデカルト積を生成することを示しています。 これは、各テーブルの行数の積を調査する必要があるため、著しく時間がかかります。 このケースの場合は、この積が 74 × 2135 × 74 × 3872 = 45,268,558,720 行になります。 テーブルがもっと大きければ、どのくらい時間がかかっていたか簡単に想像がつきます。

ここでの問題の 1 つは、カラムが同じ型とサイズで宣言されている場合に、MySQL はカラムに対してインデックスをより効率的に使用できることです。 このコンテキストでは、VARCHARCHAR は同じサイズとして宣言されている場合、それらは同じとみなされます。tt.ActualPCCHAR(10) として宣言されており、et.EMPLOYIDCHAR(15) であるため、長さの不一致があります。

このカラム長の不一致を修正するには、ALTER TABLE を使用して ActualPC を 10 文字から 15 文字に長くします。

mysql> ALTER TABLE tt MODIFY ActualPC VARCHAR(15);

これで tt.ActualPCet.EMPLOYID はいずれも VARCHAR(15) になります。 EXPLAIN ステートメントを再度実行すると、次の結果が生成されます。

table type   possible_keys key     key_len ref         rows    Extra
tt    ALL    AssignedPC,   NULL    NULL    NULL        3872    Using
             ClientID,                                         where
             ActualPC
do    ALL    PRIMARY       NULL    NULL    NULL        2135
      Range checked for each record (index map: 0x1)
et_1  ALL    PRIMARY       NULL    NULL    NULL        74
      Range checked for each record (index map: 0x1)
et    eq_ref PRIMARY       PRIMARY 15      tt.ActualPC 1

これは完全ではありませんが、はるかに改善されています。rows 値の積は 74 の係数分だけ少なくなります。 このバージョンは、数秒で実行します。

2 つめの変更を実行して、tt.AssignedPC = et_1.EMPLOYIDtt.ClientID = do.CUSTNMBR の比較でのカラム長の不一致を解消できます。

mysql> ALTER TABLE tt MODIFY AssignedPC VARCHAR(15),
                      MODIFY ClientID   VARCHAR(15);

その変更後、EXPLAIN は次に示す出力を生成します。

table type   possible_keys key      key_len ref           rows Extra
et    ALL    PRIMARY       NULL     NULL    NULL          74
tt    ref    AssignedPC,   ActualPC 15      et.EMPLOYID   52   Using
             ClientID,                                         where
             ActualPC
et_1  eq_ref PRIMARY       PRIMARY  15      tt.AssignedPC 1
do    eq_ref PRIMARY       PRIMARY  15      tt.ClientID   1

この時点で、クエリーはほぼ可能なかぎり十分に最適化されています。 残りの問題は、MySQL はデフォルトで tt.ActualPC カラムの値が均一に分布しているものと想定しますが、tt テーブルにはそれが当てはまらないことです。 さいわい、MySQL にキー分布を分析するように伝えることは簡単です。

mysql> ANALYZE TABLE tt;

追加のインデックス情報によって、結合が完全になり、EXPLAIN が次の結果を生成します。

table type   possible_keys key     key_len ref           rows Extra
tt    ALL    AssignedPC    NULL    NULL    NULL          3872 Using
             ClientID,                                        where
             ActualPC
et    eq_ref PRIMARY       PRIMARY 15      tt.ActualPC   1
et_1  eq_ref PRIMARY       PRIMARY 15      tt.AssignedPC 1
do    eq_ref PRIMARY       PRIMARY 15      tt.ClientID   1

EXPLAIN からの出力の rows カラムは、MySQL 結合オプティマイザからの教育を受けた推測です。 rows の積とクエリーが返す実際の行数を比較して、数値が実際と近いかどうかをチェックしてください。 数値がかなり異なる場合は、SELECT ステートメントで STRAIGHT_JOIN を使用し、FROM 句で異なる順序でテーブルを一覧表示してみるとパフォーマンスを改善できる可能性があります。 (ただし、STRAIGHT_JOIN では準結合変換が無効になるため、インデックスの使用が妨げられる場合があります。 セクション8.2.2.1「準結合変換による IN および EXISTS サブクエリー述語の最適化」を参照してください。)

場合によっては、サブクエリーで EXPLAIN SELECT を使用するときに、データを変更するステートメントを実行できることもあります。詳細については、セクション13.2.11.8「導出テーブル」を参照してください。


関連キーワード:  テーブル, インデックス, カラム, 結合, クエリー, EXPLAIN, key, ref, 出力, 参照