EXPLAIN
ステートメントは、MySQL がステートメントを実行する方法に関する情報を提供します。 EXPLAIN
は、SELECT
, DELETE
, INSERT
, REPLACE
および UPDATE
ステートメントで動作します。
EXPLAIN
は SELECT
ステートメントで使用される各テーブルに関する情報の行を返します。 これは、MySQL がステートメントの処理中にテーブルを読み取る順番で、出力にテーブルを一覧表示します。 これは、MySQL が最初のテーブルから行を読み取り、次に 2 番目のテーブル、3 番目のテーブルなどで一致する行を検索することを意味します。 すべてのテーブルが処理されると、MySQL は選択したカラムを出力し、さらに一致する行があるテーブルが見つかるまで、テーブルリストを逆戻りします。 次の行がテーブルから読み取られ、プロセスは次のテーブルに進みます。
MySQL Workbench には、EXPLAIN
出力を視覚的に表現する Visual Explain 機能があります。 Tutorial: Using Explain to Improve Query Performanceを参照してください。
このセクションでは、EXPLAIN
によって生成される出力カラムについて説明します。 あとのセクションで、type
と Extra
カラムに関する追加情報を提供します。
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
出力には表示されません。
-
SELECT
識別子。 これはクエリー内のSELECT
の連番です。 行がほかの行の和集合結果を参照する場合に、値はNULL
になることがあります。 この場合、table
カラムには、<union
などの値が表示され、行がM
,N
>M
およびN
のid
値のある行の和集合を参照していることが示されます。 -
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 つめ以降の SELECTDEPENDENT
は一般に、相関サブクエリーの使用を示します。 セクション13.2.11.7「相関サブクエリー」を参照してください。DEPENDENT SUBQUERY
の評価はUNCACHEABLE SUBQUERY
の評価とは異なります。DEPENDENT SUBQUERY
の場合、その外部コンテキストの変数の異なる値の各セットにつき、一回だけサブクエリーが再評価されます。UNCACHEABLE SUBQUERY
の場合、外部コンテキストの行ごとにサブクエリーが再評価されます。EXPLAIN
でFORMAT=JSON
を指定した場合、出力にはselect_type
と直接同等の単一のプロパティはありません。query_block
プロパティは特定のSELECT
に対応します。 表示されているほとんどのSELECT
サブクエリータイプに相当するプロパティが使用可能で (たとえば、materialized_from_subquery
forMATERIALIZED
)、必要に応じて表示されます。SIMPLE
またはPRIMARY
に相当する JSON はありません。SELECT
以外のステートメントのselect_type
値には、影響を受けるテーブルのステートメントタイプが表示されます。 たとえば、select_type
はDELETE
ステートメント用のDELETE
です。 -
出力の行で参照しているテーブルの名前。 これも次のいずれかの値になることがあります。
<union
: 行はM
,N
>M
およびN
のid
値のある行の和集合を参照しています。<derived
: 行はN
>N
のid
値のある行の派生テーブル結果を参照しています。 派生テーブルは、たとえばFROM
句内のサブクエリーの結果などになります。<subquery
: 行はN
>N
のid
値のある行の実体化されたサブクエリーの結果を参照しています。 セクション8.2.2.2「実体化を使用したサブクエリーの最適化」を参照してください。
-
partitions
(JSON 名):partitions
)クエリーでレコードが照合されるパーティション。 パーティション化されていないテーブルの場合、この値は
NULL
です。 セクション24.3.5「パーティションに関する情報を取得する」を参照してください。 -
結合型。 さまざまな型の説明については、「
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
カラムは、MySQL が実際に使用することを決定したキー (インデックス) を示します。 MySQL が行をルックアップするために、いずれかのpossible_keys
インデックスを使用することを決定した場合、キー値としてそのインデックスが一覧表示されます。key
は、possible_keys
値に存在しないインデックスに名前を付けることができます。 これはpossible_keys
インデックスのどれも行のルックアップに適していない場合に発生する可能性がありますが、クエリーによって選択されるすべてのカラムはほかのインデックスのカラムになります。 つまり、指定されたインデックスは選択されたカラムをカバーするため、取得する行を決定するために使用されませんが、インデックススキャンはデータ行スキャンよりも効率的です。InnoDB
は各セカンダリインデックスとともに主キー値を保存するため、InnoDB
では、クエリーで主キーも選択している場合でも、セカンダリインデックスで選択されたカラムをカバーしている可能性があります。key
がNULL
の場合、MySQL はクエリーをより効率的に実行するために使用するインデックスを見つけられませんでした。MySQL で
possible_keys
カラムに示されたインデックスを強制的に使用させるか、無視させるには、クエリーでFORCE INDEX
、USE 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
カラムは、テーブルから行を選択するために、key
カラムに指定されたインデックスに対して比較されるカラムまたは定数を示します。値が
func
の場合、使用される値は、特定の関数の結果です。 どの関数を表示するには、EXPLAIN
の後のSHOW WARNINGS
を使用して、拡張EXPLAIN
出力を表示します。 関数は、実際には算術演算子などの演算子である場合があります。 -
rows
カラムは、MySQL がクエリーを実行するために調査する必要があると考える行数を示します。InnoDB
テーブルの場合、これは推定値であり、常に正確ではないことがあります。 -
filtered
カラムは、テーブル条件でフィルタされるテーブルの行の推定割合を示します。 最大値は 100 で、これは行のフィルタリングが行われなかったことを意味します。 100 から減少する値は、フィルタリングの量が増加していることを示します。rows
には調査された推定行数が表示され、rows
×filtered
には次のテーブルと結合された行数が表示されます。 たとえば、rows
が 1000 でfiltered
が 50.00 (50%) の場合、次のテーブルと結合される行数は 1000×50% = 500 になります。 -
このカラムには、MySQL がクエリーを解決する方法に関する追加情報が含まれます。 さまざまな値の説明については、「
EXPLAIN
の追加情報」を参照してください。Extra
カラムに対応する単一の JSON プロパティはありませんが、このカラムで発生する可能性のある値は JSON プロパティまたはmessage
プロパティのテキストとして公開されます。
EXPLAIN
出力の type
カラムには、テーブルの結合方法が示されます。 JSON 形式の出力では、これらは access_type
プロパティの値として検出されます。 次のリストに、もっとも適切な型からもっとも不適切な型の順番で並べた結合型を示します。
-
テーブルには行が 1 つしかありません (= system テーブル)。 これは、
const
結合型の特殊なケースです。 -
テーブルには、一致するレコードが最大で 1 つあり、クエリーの開始時に読み取られます。 行が 1 つしかないため、この行のカラムの値は、オプティマイザの残りによって定数とみなされることがあります。
const
テーブルは、1 回しか読み取られないため、非常に高速です。const
はPRIMARY KEY
またはUNIQUE
インデックスのすべてのパートを定数値と比較する場合に使用されます。 次のクエリーでは、tbl_name
はconst
テーブルとして使用できます。SELECT * FROM tbl_name WHERE primary_key=1; SELECT * FROM tbl_name WHERE primary_key_part1=1 AND primary_key_part2=2;
-
前のテーブルの行の組み合わせごとに、このテーブルから 1 行ずつ読み取られます。
system
とconst
型以外で、これは最適な結合型です。 これは、結合でインデックスのすべてのパートが使用されており、インデックスが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
は、結合でキーの左端のプリフィクスのみが使用される場合、またはキーがPRIMARY KEY
やUNIQUE
インデックスではない場合 (つまり、結合で、キー値に基づいて単一の行を選択できない場合) に使用されます。 使用されているキーがほんの数行にしか一致しない場合、これは適切な結合型です。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
インデックスを使用して実行されます。 -
この結合型は、
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 の最適化」を参照してください。
-
この結合型はインデックスマージ最適化が使用されたことを示します。 この場合、出力行の
key
カラムには使用されたインデックスのリストが含まれ、key_len
には使用されたインデックスの最長キーパートのリストが含まれます。 詳細については、セクション8.2.1.3「インデックスマージの最適化」を参照してください。 -
このタイプは、次の形式の一部の
IN
サブクエリーでeq_ref
に置き換わります:value IN (SELECT primary_key FROM single_table WHERE some_expr)
unique_subquery
は、効率化のため、サブクエリーを完全に置き換える単なるインデックスルックアップ関数です。 -
この結合型は
unique_subquery
に似ています。IN
サブクエリーを置き換えますが、次の形式のサブクエリー内の一意でないインデックスに対して機能します。value IN (SELECT key_column FROM single_table WHERE some_expr)
-
行を選択するためのインデックスを使用して、特定の範囲にある行のみが取得されます。 出力行の
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 は、クエリーで単一のインデックスの一部であるカラムのみが使用されている場合に、この結合型を使用できます。
-
フルテーブルスキャンは、前のテーブルの行の組み合わせごとに実行されます。 これは、通常テーブルが
const
とマークされていない最初のテーブルである場合には適しておらず、通常ほかのすべてのケースで著しく不適切です。 通常、定数値または以前のテーブルからのカラム値に基づいて、テーブルからの行の取得を可能にするインデックスを追加することで、ALL
を回避できます。
EXPLAIN
出力の Extra
カラムには、MySQL がクエリーを解決する方法に関する追加情報が含まれます。 次のリストに、このカラムに表示される可能性のある値について説明します。 各セクション目は、JSON 形式の出力に対して、Extra
値を表示するプロパティも示します。 これらの一部には、特定のプロパティがあります。 その他は、message
プロパティのテキストとして表示されます。
クエリーをできるだけ高速にする場合は、Using filesort
および Using temporary
の Extra
カラムの値を検索するか、JSON 形式の EXPLAIN
出力で using_filesort
および using_temporary_table
のプロパティが true
に等しいかどうかを調べます。
-
Child of '
(JSON:table
' pushed join@1message
テキスト)このテーブルは、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(
(JSON プロパティ):tbl_name
)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(
(JSON プロパティ):m
..n
)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.id
がNOT NULL
で定義されているとします。 この場合、MySQL はt1
をスキャンし、t1.id
の値を使用してt2
内の行をルックアップします。 MySQL がt2
内に一致する行を見つけた場合、t2.id
はNULL
にならないことがわかっているため、同じ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:
(JSON プロパティ) :N
)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
(JSON プロパティ):N
databasesmessage
)これは、セクション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 temporary
、End 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
がない場合でも使用できます。 これは、type
がindex
でkey
がPRIMARY
の場合です。 -
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 Loop
、Batched 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
出力の 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)
各テーブルの type
は ALL
であるため、この出力は MySQL がすべてのテーブル、つまりすべての行の組み合わせのデカルト積を生成することを示しています。 これは、各テーブルの行数の積を調査する必要があるため、著しく時間がかかります。 このケースの場合は、この積が 74 × 2135 × 74 × 3872 = 45,268,558,720 行になります。 テーブルがもっと大きければ、どのくらい時間がかかっていたか簡単に想像がつきます。
ここでの問題の 1 つは、カラムが同じ型とサイズで宣言されている場合に、MySQL はカラムに対してインデックスをより効率的に使用できることです。 このコンテキストでは、VARCHAR
と CHAR
は同じサイズとして宣言されている場合、それらは同じとみなされます。tt.ActualPC
は CHAR(10)
として宣言されており、et.EMPLOYID
は CHAR(15)
であるため、長さの不一致があります。
このカラム長の不一致を修正するには、ALTER TABLE
を使用して ActualPC
を 10 文字から 15 文字に長くします。
mysql> ALTER TABLE tt MODIFY ActualPC VARCHAR(15);
これで tt.ActualPC
と et.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.EMPLOYID
と tt.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「導出テーブル」を参照してください。