Documentation Home
MySQL 8.0 リファレンスマニュアル
Download this Manual
PDF (US Ltr) - 36.1Mb
PDF (A4) - 36.2Mb


このページは機械翻訳したものです。

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 名意味
idselect_idSELECT 識別子。
select_typeなしSELECT
tabletable_name出力行のテーブル
partitionspartitions一致するパーティション
typeaccess_type結合型
possible_keyspossible_keys選択可能なインデックス
keykey実際に選択されたインデックス
key_lenkey_length選択されたキーの長さ
refrefインデックスと比較されるカラム
rowsrows調査される行の見積もり
filteredfilteredテーブル条件によってフィルタ処理される行の割合
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_typeJSON 名意味
    SIMPLEなし単純なSELECT (UNION やサブクエリーを使用しません)
    PRIMARYなしもっとも外側のSELECT
    UNIONなしUNION 内の 2 つめ以降のSELECT ステートメント
    DEPENDENT UNIONdependent (true)UNION 内の 2 つめ以降のSELECT ステートメントで、外側のクエリーに依存します
    UNION RESULTunion_resultUNION の結果。
    SUBQUERYなしサブクエリー内の最初のSELECT
    DEPENDENT SUBQUERYdependent (true)サブクエリー内の最初のSELECT で、外側のクエリーに依存します
    DERIVEDなし導出テーブル
    DEPENDENT DERIVEDdependent (true)別のテーブルに依存する導出テーブル
    MATERIALIZEDmaterialized_from_subquery実体化されたサブクエリー
    UNCACHEABLE SUBQUERYcacheable (false)結果をキャッシュできず、外側のクエリーの行ごとに再評価される必要があるサブクエリー
    UNCACHEABLE UNIONcacheable (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 forMATERIALIZED)、必要に応じて表示されます。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 FROMtbl_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 * FROMtbl_name WHEREprimary_key=1;SELECT * FROMtbl_name  WHEREprimary_key_part1=1 ANDprimary_key_part2=2;
  • eq_ref

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

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

    SELECT * FROMref_table,other_table  WHEREref_table.key_column=other_table.column;SELECT * FROMref_table,other_table  WHEREref_table.key_column_part1=other_table.column  ANDref_table.key_column_part2=1;
  • ref

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

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

    SELECT * FROMref_table WHEREkey_column=expr;SELECT * FROMref_table,other_table  WHEREref_table.key_column=other_table.column;SELECT * FROMref_table,other_table  WHEREref_table.key_column_part1=other_table.column  ANDref_table.key_column_part2=1;
  • fulltext

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

  • ref_or_null

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

    SELECT * FROMref_table  WHEREkey_column=expr ORkey_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 (SELECTprimary_key FROMsingle_table WHEREsome_expr)

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

  • index_subquery

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

    value IN (SELECTkey_column FROMsingle_table WHEREsome_expr)
  • range

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

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

    SELECT * FROMtbl_name  WHEREkey_column = 10;SELECT * FROMtbl_name  WHEREkey_column BETWEEN 10 and 20;SELECT * FROMtbl_name  WHEREkey_column IN (10,20,30);SELECT * FROMtbl_name  WHEREkey_part1 = 10 ANDkey_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 ... FROMtbl_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 ... WHEREcondition などのクエリーの条件を満たす行がありません。

  • 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 JOINtable_b ONcondition を補完したものです。 アンチ結合では、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 の新しい行が処理されるたびに最新になるように再実体化されます。

  • ScannedN 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 ... FROMtbl_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カラムデータ型
    ttActualPCCHAR(10)
    ttAssignedPCCHAR(10)
    ttClientIDCHAR(10)
    etEMPLOYIDCHAR(15)
    doCUSTNMBRCHAR(15)
  • テーブルには次のインデックスがあります。

    Tableインデックス
    ttActualPC
    ttAssignedPC
    ttClientID
    etEMPLOYID (主キー)
    doCUSTNMBR (主キー)
  • tt.ActualPC 値は均一に分布されていません。

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

table type possible_keys key  key_len ref  rows  Extraet    ALL  PRIMARY       NULL NULL    NULL 74do    ALL  PRIMARY       NULL NULL    NULL 2135et_1  ALL  PRIMARY       NULL NULL    NULL 74tt    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    Extratt    ALL    AssignedPC,   NULL    NULL    NULL        3872    Using             ClientID,                                         where             ActualPCdo    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 Extraet    ALL    PRIMARY       NULL     NULL    NULL          74tt    ref    AssignedPC,   ActualPC 15      et.EMPLOYID   52   Using             ClientID,                                         where             ActualPCet_1  eq_ref PRIMARY       PRIMARY  15      tt.AssignedPC 1do    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 Extratt    ALL    AssignedPC    NULL    NULL    NULL          3872 Using             ClientID,                                        where             ActualPCet    eq_ref PRIMARY       PRIMARY 15      tt.ActualPC   1et_1  eq_ref PRIMARY       PRIMARY 15      tt.AssignedPC 1do    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「導出テーブル」を参照してください。