MySQLのEXPLAINでは関連する各テーブルごとにそれぞれ情報が出力される。EXPLAINで表示されている順序がJOINの順序を表している。すなわち、ここではCountryテーブルが駆動表、Cityテーブルが内部表ということになる。SELECTで指定した順序とは逆になっているのは、オプティマイザによってSELECTで記述されたものとは異なる順序が選択されたという点に留意されたい。NLJではJOINの順序が重要なポイントであるが、EXPLAINを実行すればひと目で分かるのである。
次に読み取るべきなのは、NLJにおいてもうひとつのポイントであるインデックスに関する情報だ。上記の例では内部表はCityテーブルであり、JOINが効率的であるかを判断するにはCityテーブルがインデックスが効果的に使われているかどうかが判断基準となる。possible_keysフィールドは使用可能なインデックスの一覧だ。この例ではPRIMARY(主キー)しか表示されていないが、これはCityテーブルにはそもそも主キーしか存在しないため他に挙げるべきインデックスがないからである。テーブルに複数のインデックスがある場合には複数のインデックスが列挙されることになる。keyフィールドは利用可能なインデックスのうち、実際に選択されたインデックスを示す。ここでもPRIMARYが選択されており、内部表へのアクセスには主キーが用いられることが分かる。key_lenフィールドは選択されたインデックスのキー長である。
テーブルへのアクセスがどのように行われるかを、より詳細に知るにはtypeフィールドとrefフィールドを見る。typeフィールドは別名レコードアクセスタイプと呼ばれ、まさしくテーブルへアクセスする方法の種類を示す。レコードアクセスタイプには次の種類がある。
タイプ | 説明 |
const | PRIMARY KEYまたはUNIQUEインデックスのルックアップ(等価比較)によるアクセス |
eq_ref | JOINにおいて内部表から行のフェッチがPRIARY KEYまたはUNIQUE KEYにのルックアップよって行われることを示す。constと似ているがJOINで用いられるところが違う |
ref | ユニーク(PRIMARY or UNIQUE)でないインデックスを使って等価検索(WHERE key = value)を行った時に使われるアクセスタイプ |
range | インデックスを用いた範囲検索 |
ALL | フルテーブルスキャン |
index | フルインデックススキャン |
refフィールドにはインデックスと比較されるフィールドの名前が表示される。この例のSELECTでは結合条件は「ON (City.Id = Country.Capital)」なので、refフィールドは「world_innodb.Country.Capital」となっている。ちなみに、駆動表でインデックスがリテラル値によって検索される場合には、refフィールドはconstとなる。
Extraフィールドはオプティマイザがどのような判断を行ったかということに関するヒントが表示される。オプティマイザの独り言のようなものであるが、オプティマイザの挙動を把握する上で実は見逃せないフィールドである。この例では「Using where」と表示されているが、これはテーブルからレコードをフェッチしてからWHERE句の検索条件によって絞り込みを行うことを示す。インデックスを用いたアクセスであっても、カラムがNOT NULLに設定されていないと出力される。これはよく出るヒントであり、恐らくMySQLでEXPLAINを実行したことがある人ならば必ず一度は「Using where」の文字を目にしたことがあるだろう。紙面の都合上、Extraフィールドのその他の値についての解説は今回割愛する。
あまり重要な情報ではないが、idフィールドとselect_typeフィールドについても解説しておこう。idフィールドはSELECTを構成するJOINにそれぞれつけられる。上記のクエリは2つのテーブルをJOINしているので、SELECTの構成単位であるJOINはひとつしかない。そのため全てのテーブルで共通のidである「1」が割り当てられている。次に、select_typeフィールドはSIMPLEとなっているが、これはSELECTの種類がJOINであることを示すものである。どれだけ複雑なJOINが行われていても、いくつのテーブルをJOINしても、サブクエリやUNIONが含まれていなければSIMPLEとなる。