このセクションではWHERE節をプロセスする際の最適化方法を記述します。WHERE
の最適化は、ほとんどの場合
SELECTとともに使用されるため、SELECT部分に適用されますがDELETEや
UPDATE のステートメントの
WHEREにも同じ最適化が適用されます。
また、このセクションは完全なものではないため、注意が必要です。MySQL は多様な最適化を実行するため、すべてを文書化するには時間が足りませんでした。
MySQL によって実行される最適化の一部をここに紹介します
不要なかっこの削除。
((a AND b) AND c OR (((a AND b) AND (c AND d)))) -> (a AND b AND c) OR (a AND b AND c AND d)
定数の折りたたみ。
(a<b AND b=c) AND a=5 -> b>5 AND b=c AND a=5
定数条件の削除(定数の折りたたみに必要)。
(B>=5 AND B=5) OR (B=6 AND 5=5) OR (B=7 AND 5=6) -> B=5 OR B=6
インデックスが使用する定数式が評価されるのは、1 回に限られる。
COUNT(*)
WHEREがない単一テーブルの
COUNT(*) は、MyISAMと HEAP
テーブルのテーブル情報から直接取り出される。
これは、テーブル 1
つのみで使用する場合はすべての NOT
NULL式でも実行される。
無効定数式の早期検出。MySQL は実行不可能な
SELECTステートメントがある場合、それを迅速に検出し、結果としてレコードを返さない。
GROUP
BYまたはグループ関数(COUNT()、MIN())を使用しない場合、HAVINGは
WHEREとマージされる。
サブ結合のそれぞれに、単純な
WHEREが構造化され、サブ結合ごとに迅速に
WHERE評価を取得し、可能な限り迅速にレコードをスキップする。
クエリ内の他のすべてのテーブルの前に、まず、すべての定数テーブルが読み込まれる。定数テーブルとは以下のものを指す。
空白テーブルまたは 1 行のみのテーブル。
UNIQUEインデックスまたは
PRIMARY KEYを使う
WHERE節とともに使用されるテーブルで、インデックス部分のすべてが定数式とともに使用され、そのインデックス部分が
NOT
NULLとして定義されている場合。
以下のテーブルはすべて定数テーブルとして使用される。
SELECT * FROM t WHEREprimary_key=1; SELECT * FROM t1,t2 WHERE t1.primary_key=1 AND t2.primary_key=t1.id;
テーブルを結合する最適な結合の組み合わせは、すべての可能性を試行してみることで発見される。ORDER
BYおよび GROUP
BY内の全てのカラムが 1
つのテーブルに存在する場合、結合を行う時は第一にこのテーブルが選ばれる。
ORDER BY節とそれと異なる
GROUP
BY節がある場合、あるいは、ORDER
BYまたは GROUP
BYに結合キューの第 1
テーブルとは異なるテーブルのカラムが含まれている場合は、テンポラリテーブルが作成される。
SQL_SMALL_RESULTを使用する場合、MySQL
ではメモリ内のテンポラリテーブルが使用される。
オプティマイザがテーブルスキャンをしたほうが効率的と判断しない限り、テーブルインデックスごとにクエリが行われ、スパンがレコードの 30% 以上である最適インデックスが使用される。しかし、現在では一定の率でインデックスかスキャンを使用するか判断されます。今バージョンのオプティマイザはより複雑で、テーブルサイズ、行数、そしてI/Oブロックサイズを基準に推定します。
状況によっては、MySQL でデータファイルの参照もせずにインデックスからレコードを読み取れる場合もある。インデックスから使用されるカラムのすべてが数値型の場合、クエリの解決にはインデックスツリーのみが使用される。
レコードのそれぞれが出力される前に、HAVING節と一致しないレコードはスキップされる。
非常に高速なクエリのサンプルをいくつか紹介します。
SELECT COUNT(*) FROMtbl_name; SELECT MIN(key_part1),MAX(key_part1) FROMtbl_name; SELECT MAX(key_part2) FROMtbl_nameWHEREkey_part1=constant; SELECT ... FROMtbl_nameORDER BYkey_part1,key_part2,... LIMIT 10; SELECT ... FROMtbl_nameORDER BYkey_part1DESC,key_part2DESC, ... LIMIT 10;
MySQLは次のクエリで、インデックスツリーのみを使用して解決します(インデックスのあるカラムが数値型であると想定)。
SELECTkey_part1,key_part2FROMtbl_nameWHEREkey_part1=val; SELECT COUNT(*) FROMtbl_nameWHEREkey_part1=val1ANDkey_part2=val2; SELECTkey_part2FROMtbl_nameGROUP BYkey_part1;
次のクエリは、ソートのパスを分けることなく、ソートしたレコードを取り出すためにインデックスを使用します。
SELECT ... FROMtbl_nameORDER BYkey_part1,key_part2,... ; SELECT ... FROMtbl_nameORDER BYkey_part1DESC,key_part2DESC, ... ;
