EXPLAIN
ステートメントは
DESCRIBE
の同義語として使用するか、MySQL がどのように
SELECT
ステートメントを実行するかの情報が得られます。
EXPLAIN
は
tbl_name
DESCRIBE
または
tbl_name
SHOW COLUMNS FROM
の同義語です。
tbl_name
EXPLAIN tbl_name
SELECT
ステートメントの前にキーワード
EXPLAIN
を
置いた場合、MySQL
はクエリーの実行計画に関するオプティマイザからの情報を表示します。つまり、テーブルの結合状況と順序に関する情報など、SELECT
の処理方法が説明されます。
EXPLAIN [EXTENDED | PARTITIONS] SELECT select_options
EXPLAIN
PARTITIONS
は MySQL 5.1.5
から提供されています。区割りされたテーブルのクエリーを調べるときに便利です。詳細については、Obtaining Information About Partitionsをご参照ください。
この節では、クエリー実行情報を得るための
EXPLAIN
の 2
つめの使用方法を記述します。項8.3.2. 「EXPLAIN
構文」
も参照してください。DESCRIBE
と SHOW COLUMNS
ステートメントの詳細については、項8.3.1. 「DESCRIBE
構文」と項8.5.5.6. 「SHOW COLUMNS
構文」を参照してください。
EXPLAIN
を利用すると、より速くレコードを検索する
SELECT
を得るために、テーブルのどこにインデックスを追加しなければならないかを確認できます。また、EXPLAIN
を使用して、オプティマイザがテーブルを最適な順序で結合しているかどうかも確認することができます。特定の順番で結合を行うようにオプティマイザにヒントを与えるには、ただ
SELECT
でステートメントを始めるのではなく、SELECT
ステートメントに
STRAIGHT_JOIN
節を追加します。詳細は、項8.2.8. 「SELECT
構文」
を参照してください。
最適化方法の選択に影響を及ぼすキーの、カーディナリティなどのテーブル統計を更新するために、ANALYZE
TABLE
を定期的に実行する必要があります。項8.5.2.1. 「ANALYZE TABLE
構文」
を参照してください。
EXPLAIN
は
SELECT
ステートメントで使用される各テーブルに関する情報を返します。テーブルは、読み取られた順序に従って一覧表示されます。MySQL
は、単一スイープ多結合メソッドを使用してすべての結合を解決します。これは、MySQL
が最初のテーブルからレコードを読み取ってから、第
2 のテーブル、第 3
のテーブルといった順序で、一致するレコードの検索を行うことを意味します。すべてのテーブルの処理が終わると、選択したカラムと、さらに一致レコードがあるテーブルが検索されるまでのテーブル一覧のバックトラックが出力されます。次のレコードはこのテーブルから読み取られ、処理が次のテーブルから続行されます。
EXTENDED
キーワードが使用されたとき、EXPLAIN
は SHOW WARNINGS
ステートメントを
EXPLAIN
ステートメントのあとで発行することで閲覧できる余分な情報を表示する。この情報は、SELECT
ステートメント内でオプティマイザがどのようにテーブル名とカラム名を認証するか、SELECT
が再書き込みと最適化規則の適用後どのように表示されるか、そして最適化プロセスの他の注意点なども表示します。EXPLAIN
EXTENDED
は MySQL 5.1.12
以降、filtered
カラムも表示します。
EXTENDED
と
PARTITIONS
キーワードを、同じ
EXPLAIN
ステートメントで使用することはできません。
EXPLAIN
の各出力行は 1
つのテーブルの情報を提供し、各行は次のカラムを含んでいます。
id
SELECT
識別子。クエリー内におけるこの
SELECT
の順序番号。
select_type
SELECT
節の種類。次のいずれかになります。
SIMPLE |
単純な SELECT
(UNION
やサブクエリーを使用しない)。 |
PRIMARY |
最外部の SELECT 。 |
UNION |
内の第 2 およびそれ以降の
SELECT
ステートメント。
|
DEPENDENT UNION |
UNION 内の第 2
およびそれ以降の
SELECT
ステートメント。外側のクエリーに依存する。 |
UNION RESULT |
UNION の結果。 |
SUBQUERY |
サブクエリー内の第一
SELECT 。 |
DEPENDENT SUBQUERY |
第
1SELECT 、外側のサブクエリーに依存する。 |
DERIVED |
派生テーブル SELECT
(FROM
節内のサブクエリー) |
UNCACHEABLE SUBQUERY |
結果がキャッシュされず、外側のクエリーの各行ごとに再評価されるサブクエリー。 |
UNCACHEABLE UNION |
キャッシュ不可能なサブクエリーの
UNION
内の第 2 およびそれ以降の SELECT
(UNCACHEABLE
SUBQUERY を参照) |
DEPENDENT
は主に、相互に関係するサブクエリーの使用を表します。項8.2.9.7. 「相関サブクエリー」
を参照してください。
「依存型サブクエリー」の評価は
UNCACHEABLE SUBQUERY
評価とは異なります。「DEPENDENT
SUBQUERY」
に関しては、外側コンテキストの変数の値が異なるたびに、一回のみサブクエリーの再評価が行われます。UNCACHEABLE
SUBQUERY
に関しては、サブクエリーは外側コンテキストの各行ごとに再評価されます。サブクエリーのキャッシュアビリティは項4.5.5.1. 「クエリキャッシュの動作」で記述される制限によります。たとえば、ユーザー変数に参照することでサブクエリーがキャッシュできなくなります。
table
結果を得るために参照するテーブル。
type
結合型。各結合型を最適なものから順に紹介する。
1 レコードのみで構成されるテーブル (=
システムテーブル)。これは、const
結合型の特殊なケースである。
テーブルに、一致するレコードが最大で 1
つあり、クエリーの開始時に読み取られる。レコードが
1
つしかないため、このレコードのカラムの値はオプティマイザによって定数と見なされます。const
テーブルは、1
回しか読み取られないため、非常に高速です。
const
は PRIMARY
KEY
/UNIQUE
キーを定数と比較する場合に使用される。下記のクエリーでは、tbl_name
は
const
テーブルとして使用できる。
SELECT * FROMtbl_name
WHEREprimary_key
=1; SELECT * FROMtbl_name
WHEREprimary_key_part1
=1 ANDprimary_key_part2
=2;
前のテーブルのレコードの組み合わせのそれぞれに対して、このテーブルから
1
レコードずつ読み取られる。これは、system
と
const
型以外で最適な結合型である。結合でインデックスのすべての部分が使用され、このインデックスが
UNIQUE
または
PRIMARY KEY
である場合に使用される。
=
演算子と比較されるインデックスの張られたカラムには、eq_ref
を使用できる。比較対象のアイテムは定数でも、このテーブル以前に読み取られたテーブルのカラムを使用する式でもかまわない。下記の例では、ref_table
で
eq_ref
が使用される。
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
は、インデックスの左端の接頭辞のみが結合で使用される場合、またはインデックスが
PRIMARY KEY
や
UNIQUE
インデックスではない場合
(すなわち、この結合において、インデックス値から
1 つのレコードを SELECT できない場合)
に使用されます。この結合型は、使用されるインデックスと一致するレコードが数レコードしかない場合に適している。
=
あるいは<=>
演算子と比較されるインデックスの張られたカラムには、ref
を使用できる。下記の例では、MySQL は
ref_table
で
ref
が使用される。
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
インデックスを使用して結合が実行されます。
ref
と同様だが、NULL
を使用したレコードの補足検索も追加で実行される。この結合型の最適化は主としてサブクエリーを解決する場合に使用される。下記の例では、MySQL
は ref_table
で
ref_or_null
が使用される。
SELECT * FROMref_table
WHEREkey_column
=expr
ORkey_column
IS NULL;
項4.2.8. 「IS NULL
最適化」
を参照してください。
この結合型はインデックス併合最適化が使用されたことを示しています。この場合、出力行の
key
カラムは使用されたインデックスのリストが含まれ、key_len
には使用されたインデックスの最長キーパートが含まれます。詳細は
項4.2.6. 「インデックス結合最適化」
をご覧ください。
この型は、下記のフォームで
IN
サブクエリーの代わりに、ref
を使用します。
value
IN (SELECTprimary_key
FROMsingle_table
WHEREsome_expr
)
unique_subquery
は、効率化のためサブクエリーの代わりをつとめるインデックスルックアップ関数です。
この結合型は
unique_subquery
に似ています。IN
サブクエリーの代わりに使用されますが、次の形式のサブクエリーで一意でないインデックスで使用できます。
value
IN (SELECTkey_column
FROMsingle_table
WHEREsome_expr
)
インデックスを使用して、一定の範囲にあるレコードのみが取り出される。key
カラムに使用されるインデックスが示される。key_len
_には使用される最長のインデックス部分が記載される。この型では
ref
カラムが
NULL
になる。
range
は、=
、<>
、>
、>=
、<
、<=
、IS
NULL
、<=>
、BETWEEN
、または
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);
これは、インデックスツリーのみがスキャンされる点を除いて
ALL
と同じである。一般にインデックスファイルはデータファイルより小さいため、通常は
ALL
より高速である。
MySQL は、クエリーで 1 インデックスの構成部分であるカラムのみが使用される場合にのみ使用できます。
前のテーブルのレコードの組み合わせのそれぞれに対して、フルテーブルスキャンが実行される。一般に、テーブルが
const
の指定がない第 1
テーブルの場合には適さず、その他の場合はすべて非常に不適である。通常は、さらにインデックスを追加することで
ALL
を回避し、定数値または以前のテーブルのカラム値を基準にレコードを取り出すようにすることができる。
possible_keys
possible_keys
カラムは、このテーブル内のレコードの検索に
MySQL
で使用可能なインデックスを示す。このカラムは
EXPLAIN
からの出力により表示されたテーブルの順序にはまったく依存しないことに注意する。すなわち、possible_keys
のキーの一部は、生成されたテーブルの順序では事実上使用できないことになる。
このカラムが NULL
の場合は、対応するインデックスがない。t
この場合は、WHERE
節でインデックス設定に適するカラムを 1
つ以上参照しているかどうかを調べることでクエリーのパフォーマンスを改善できる。参照している場合は適切なインデックスを作成し、再度
EXPLAIN
を使用してクエリーを確認する。項8.1.7. 「ALTER TABLE
構文」
を参照してください。
テーブルにあるインデックスを調べるには、SHOW
INDEX FROM
を使用します。
tbl_name
key
key
カラムは、MySQL
が実際に使用を決定したキー (インデックス)
を示す。MySQL が行をルックアップするため
possible_keys
インデックスを使用した場合、キー値としてそのインデックスがリストされる。
key
は
possible_keys
値に存在しないインデックスを指名する可能性もあります。これは
possible_keys
インデックスのうちどれも行をルックアップするのに適していない場合におこりますが、クエリーに選択されたすべてのカラムはほかのインデックスのカラムになります。つまり、指名されたインデックスが選択されたカラムをカバーします。どの行を取得するか判別するのに使用されていなくとも、データ行スキャンよりもインデックススキャンの方が効率的です。
InnoDB
では、クエリーが主キーを選択していても二次インデックスが選択されたカラムをカバーするかもしれません。これはクエリーが主キーを選択した場合もありえるのは、InnoDB
が各二次インデックスと共に主キー値も保存するからです。MySQL
がクエリーを効率的に実行するインデックスを見つけられなかった場合、この
key
は
NULL
になる。
MySQL で possible_keys
カラムに記載されたキーが使用されるように強制するには、クエリーで
FORCE
INDEX
、USE
INDEX
、または IGNORE
INDEX
を使用する。項8.2.8.2. 「インデックスヒントの構文」
を参照してください。
MyISAM
テーブルには、ANALYZE
TABLE
を実行することでオプティマイザでより適したインデックスを選択する際役立つ。MyISAM
テーブルに関しても、myisamchk
--analyze
は同じことをします。項8.5.2.1. 「ANALYZE TABLE
構文」、MyISAM
Table Maintenance and Crash Recovery
を参照してください。
key_len
key_len
カラムは、MySQL
が実際に使用を決定したキーの長さを示す。key
が NULL
の場合、この長さは
NULL
になる。key_len
の値によって、複合キーで MySQL
が実際に使用するパート数が示されることに注意する。
ref
ref
カラムは、テーブルからレコードを選択する際に
key
とともに使用されるカラムまたは定数を示す。
rows
rows
カラムは、クエリーの実行に際して調べる必要があると
MySQL
によって判定されたレコードの数を示す。
InnoDB
テーブルの場合、これは推定値であり、常に正確というわけではありません。
filtered
filtered
カラムはテーブルの状態によってフィルターされるテーブル行のパーセンテージ
(予想)
を表示します。つまり、rows
は検査された行の予想数を表示し、rows
× filtered
/
100
は前のテーブルと結合する行の数を表示します。EXPLAIN
EXTENDED
を使用すると、このカラムが表示されます。(MySQL
5.1.12 の新しい機能です)。
Extra:
このカラムには、MySQL
でどのようにクエリーが解決されるかに関する追加情報が記載される。下記のリストはこのカラムで表示される可能性のある値を説明する。クエリーの速度をできるかぎり上げたい場合は、Using
filesort
と Using
temporary
の
Extra
値に注目してください。
Distinct
マッチした最初のレコードが検索されると、MySQL は現在のレコードの組み合わせによるその後のレコード検索を続行しないことを示す。
Full scan on NULL key
これは、オプティマイザでインデックス検索アクセスメソッドを使用できない場合の代替方針として、サブクエリーの最適化に使用されます。
const tables を読んだ後
Impossible WHERE 発見
MySQL はすべての
const
(あと、system
)
テーブルを読んだ後、WHERE
節が常に偽となります。
No tables
クエリーには
FROM
節がないか、FROM
DUAL
節があります。
Not exists
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
ではないと認識され、t2
内の同じ id
を持つ残りのレコードのスキャンは行われない。言い換えると、t2
にあるマッチするレコードの数に関わらず、MySQL
で実行が必要なことは
t1
のレコードのそれぞれに対して、t2
のルックアップを 1
回実行することだけである。
Range checked for each record
(index map:
N
)
MySQL
で使用に適した実際のインデックスを検索できなかったことを示す。代替として、先行テーブルのレコードの組み合わせのそれぞれに対して、使用するインデックス
(存在する場合)range
または
index_merge
の確認が実行され、このインデックスがテーブルからのレコードの取り出しに使用される。非常に高速ではないが、インデックスなしの結合と比較すると高速である。適用基準は項4.2.5. 「Range 最適化」と項4.2.6. 「インデックス結合最適化」で説明されています。ただし、これは前テーブルのすべてのカラム値が知られており、定数であるという前提においてです。
インデックスには、テーブルの
SHOW
INDEX
で表示されるのと同じ順序で
1
から番号がふられます。インデックスマップ値
N
は、どのインデックスが対象になるかを示すビットマスク値です。たとえば、0x19
(2 進数の 11001) という値は、インデックス
1、4、および 5
が対象になることを示します。
Scanned
N
databases
サーバーが
INFORMATION_SCHEMA
テーブルのクエリーを処理する際に実行するディレクトリスキャンの数を示します。詳細については、項4.2.20. 「INFORMATION_SCHEMA
最適化」を参照してください。N
の値は 0、1、または
all
です。
Select tables optimized
away
クエリーは
MyISAM
用に、インデックスで解決された集約ファンクション
(MIN()
、MAX()
)
そして COUNT(*)
があり、GROUP BY
節は含みませんでした。オプティマイザは
1
つの行のみが返されるべきと判断しました。
Skip_open_table
、Open_frm_only
、Open_trigger_only
、Open_full_table
これらの値は、INFORMATION_SCHEMA
テーブルのクエリーに適用する、ファイルを開く処理の最適化を示します。詳細については、項4.2.20. 「INFORMATION_SCHEMA
最適化」を参照してください。
Skip_open_table
:
テーブルファイルを開く必要はありません。データベースディレクトリをスキャンすることによってクエリーで情報はすでに取得されています。
Open_frm_only
:
テーブルの
.frm
ファイルのみ開く必要があります。
Open_trigger_only
:
テーブルの
.TRG
ファイルのみ開く必要があります。
Open_full_table
:
情報検索は最適化されません。.frm
、.MYD
、および
.MYI
ファイルを開く必要があります。
Using filesort
レコードをソートして取り出す方法を決定するには、MySQL
はパスを余分に実行しなくてはならないことを示す。join
type
に従ってすべてのレコードをスキャンし、WHERE
条件に一致するすべてのレコードに、ソートキー
+
行ポインタを格納して、ソートは実行される。その後キーがソートされる。最後に、ソートされた順にレコードが取り出される。項4.2.13. 「ORDER BY
最適化」
を参照してください。
Using index
インデックスツリーの情報のみを使用してカラム情報がテーブルから取り出され、実際の行を読み取るその後の検索を実行する必要がないことを示す。この方針は、クエリーで単一のインデックスの構成部分であるカラムのみが使用される場合に使用できます。
ユーザー定義のクラスタ化されたインデックスを持つ
InnoDB
テーブルの場合、そのインデックスは
Extra
カラムに
Using index
がない場合でも使用できます。type
が
index
で key
が
PRIMARY
の場合はこれに当てはまります。
Using index for
group-by
Using index
テーブルアクセスメソッドに似て、Using
index for group-by
は MySQL
が余分なディスクアクセスを実際のテーブルに行うことなく、GROUP
BY
または
DISTINCT
クエリーのカラムをすべて取得することができるインデックスを見つけたことを意味します。加えて、インデックスは各グループにとってもっとも効率的に使われるので、数種類のインデックスしか読まれません。詳細については、項4.2.14. 「GROUP BY
最適化」をご参照ください。
Using join buffer
それまでに結合されたテーブルは部分ごとに結合バッファーに読み込まれ、それらのバッファー内の行を使用して、現在のテーブルとの結合が実行されます。
Using
sort_union(...)
、Using
union(...)
、Using
intersect(...)
これらは
index_merge
結合型でインデックススキャンがどのように併合されるかを示しています。項4.2.6. 「インデックス結合最適化」
を参照してください。
Using temporary
クエリーの解決に MySQL
で結果を保持する一時テーブルの作成が必要であることを示す。これは一般に、GROUP
BY
を実行したカラムセットと異なるカラムセットに対して
ORDER BY
を実行した場合に発生する。
Using where
次のテーブルとの一致が調べられるレコードまたはクライアントに送信されるレコードの限定に
WHERE
節が使用されることを示す。この情報がなく、Extra
の値が Using where
ではなく、テーブルの型が
ALL
または
index
である場合はクエリーが正常に実行されないことがある
(テーブルのすべてのレコードの取得や検査を意図していない場合)。
Using where with pushed
condition
このアイテムは
NDBCLUSTER
テーブルにのみ適用されます。それは、MySQL
Cluster
がコンディションプッシュダウン最適化を使用して、インデックスのないカラムと定数の直接比較の効率化を図ることを意味します。そのような場合、条件はクラスタのデータノードに
「プッシュダウン」
され、すべてのデータノードで同時に評価されます。これは一致しない行をネットワーク上で送る必要をなくし、コンディションプッシュダウンを使える状態にあるが使用してないケースに比べて、クエリーの速度を
5 - 10 倍に増やします。詳細は
項4.2.7. 「コンディションプッシュダウン最適化」
をご覧ください。
EXPLAIN
出力の
rows
カラムのすべての値を掛け算することで、結合がどの程度適しているかを示す指針を取得できます。This
これは、クエリーの実行時に MySQL
で調べる必要があるレコード数の概要を示します。この数値は、max_join_size
変数でクエリーを制限する際にも使用されるほか、どのマルチテーブル
SELECT
ステートメントを実行するか、あるいはアボートするかを判別します。項4.5.3. 「サーバーパラメータのチューニング」
を参照してください。
下記の例は、EXPLAIN
によって得られた情報を使用して、マルチテーブル
join
を累進的に最適化する方法を示しています。
ここでは、EXPLAIN
を使用して、SELECT
ステートメントを調べるとします。
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;
この例では次のように想定しています。
比較対象のカラムは次のように宣言されています。
テーブル | カラム | Data Type |
tt |
ActualPC |
CHAR(10) |
tt |
AssignedPC |
CHAR(10) |
tt |
ClientID |
CHAR(10) |
et |
EMPLOYID |
CHAR(15) |
do |
CUSTNMBR |
CHAR(15) |
テーブルには次のインデックスがあります。
テーブル | インデックス |
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
節でテーブルの順序を変えて一覧表示してみるとパフォーマンスを改善できます。
場合によっては、EXPLAIN
SELECT
をサブクエリーとともに使用するときに、データを変更するステートメントを実行できることもあります。詳細については、項8.2.9.8. 「FROM
節内のサブクエリー」を参照してください。