このセクションでは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_name
WHEREkey_part1
=constant
; SELECT ... FROMtbl_name
ORDER BYkey_part1
,key_part2
,... LIMIT 10; SELECT ... FROMtbl_name
ORDER BYkey_part1
DESC,key_part2
DESC, ... LIMIT 10;
MySQLは次のクエリで、インデックスツリーのみを使用して解決します(インデックスのあるカラムが数値型であると想定)。
SELECTkey_part1
,key_part2
FROMtbl_name
WHEREkey_part1
=val
; SELECT COUNT(*) FROMtbl_name
WHEREkey_part1
=val1
ANDkey_part2
=val2
; SELECTkey_part2
FROMtbl_name
GROUP BYkey_part1
;
次のクエリは、ソートのパスを分けることなく、ソートしたレコードを取り出すためにインデックスを使用します。
SELECT ... FROMtbl_name
ORDER BYkey_part1
,key_part2
,... ; SELECT ... FROMtbl_name
ORDER BYkey_part1
DESC,key_part2
DESC, ... ;