参照クエリー新しく作成された順にスレッド⼀覧を表⽰するクエリーSELECT thread_id /*アンカータグでリンクを張るのに使う */, thread_titleFROM threadORDER BY thread_created DESCスレッドを詳細表⽰するクエリーSELECT thread.thread_title,thread.thread_owner,thread.thread_owner_email,thread.thread_created,thread.comment_count,thread.last_posted,comment.comment_number,comment.comment_owner,comment.comment_owner_email,comment.comment_posted,comment.comment_bodyFROM thread JOIN comment USING (thread_id)WHERE thread.thread_id = 1 /* スレッド一覧からthread_idをパラメーターでもらう */ORDER BY comment.comment_number70/144
72.
更新クエリースレッドを追加するためのクエリーINSERT INTO threadSET thread_id= 1,thread_title= 'new_thread',thread_owner= 'yoku0825',thread_owner_email= 'yoku0825@gmail.com',thread_created= '2015/04/30 19:00:00',comment_count= 1,last_posted= '2015/04/30 19:00:00'INSERT INTO comment SET thread_id= 1,comment_number= 1,comment_owner= 'yoku0825',comment_owner_email= 'yoku0825@gmail.com',comment_posted= '2015/04/30 19:00:00',comment_body= 'This is my first thread!'71/144
73.
更新クエリーコメントを追加するクエリーINSERT INTO commentSET thread_id= 1,comment_number= 1,comment_owner= 'yoku0825',comment_owner_email= 'yoku0825@gmail.com',comment_posted= '2015/04/30 19:00:00',comment_body= 'This is my first thread!'UPDATE thread SET comment_count= comment_count + 1,last_posted = '2015/04/30 19:00:00'WHERE thread_id = 172/144
これがテーブルスキャンmysql55> explain SELECT* FROM card WHERE suite= 'heart' AND number= 1;+----+-------------+-------+------+---------------+------+---------+------+------+-------------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |+----+-------------+-------+------+---------------+------+---------+------+------+-------------+| 1 | SIMPLE | card | ALL | NULL | NULL | NULL | NULL | 100 | Using where |+----+-------------+-------+------+---------------+------+---------+------+------+-------------+1 row in set (0.02 sec)type: ALL がテーブルスキャン(=テーブルの先頭から末尾まで全スキャン)を,rows: 100が100⾏フェッチしたことを⽰している95/144
これがスキャンソートmysql55> explain SELECT* FROM card WHERE suite= 'heart' ORDER BY number DESC;+----+-------------+-------+------+---------------+------+---------+------+------+-----------------------------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |+----+-------------+-------+------+---------------+------+---------+------+------+-----------------------------+| 1 | SIMPLE | card | ALL | NULL | NULL | NULL | NULL | 100 | Using where; Using filesort |+----+-------------+-------+------+---------------+------+---------+------+------+-----------------------------+Extra: Using filesortが⾏フェッチ後にクイックソートしていることを表す表⽰。106/144
ORDER BYのみインデックスで解決できるパターンmysql55> explainSELECT * FROM card WHERE suite= 'heart' ORDER BY number DESC;+----+-------------+-------+------+---------------+------+---------+------+------+-----------------------------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |+----+-------------+-------+------+---------------+------+---------+------+------+-----------------------------+| 1 | SIMPLE | card | ALL | NULL | NULL | NULL | NULL | 100 | Using where; Using filesort |+----+-------------+-------+------+---------------+------+---------+------+------+-----------------------------+テーブルスキャンになっちゃった(´・ω・`)並べ替えはかっ⾶ばせても、100枚全部フェッチしなきゃいけないなら、セカンダリーキーを使わずに最初からテーブルスキャンした⽅が速いとオプティマイザーが判断。わざわざ表をチェックして開く順番を⾒てから100枚もひっくり返さないといけないのは確かに⾺⿅⾺⿅しい。。-113/144
115.
インデックスでソートのみ無効化して⾼速化が⾒込めるケースmysql55> explain SELECT* FROM card FORCE INDEX(idx_number) WHERE suite= 'heart' ORDER BY number DESC LIMIT 5;+----+-------------+-------+-------+---------------+------------+---------+------+------+-------------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |+----+-------------+-------+-------+---------------+------------+---------+------+------+-------------+| 1 | SIMPLE | card | index | NULL | idx_number | 1 | NULL | 5 | Using where |+----+-------------+-------+-------+---------------+------------+---------+------+------+-------------+並べ替えの結果5件だけ戻すような場合。先頭から順番に取っていって、WHERE句にマッチするものが5件あつまった時点で残りの束を⾒る必要がなくなるため、⾏のフェッチ(ひっくり返して中を⾒る)動作はその時点でブレークできる。なのでEXPLAINで⾒ると微妙な感じがするが、件数が多くなれば多くなるほど実感できるほど⾼速化する。5.6以降のオプティマイザーは⾃動で選ぶことがあるが、5.5以前のオプティマイザーはまず間違いなくこの実⾏計画を選んでくれないLIMIT句はexecutorの範疇で、optimizerを通過した後のステージだから…という。-114/144
116.
複合インデックスの弱点左側のカラムだけ使おうとした場合mysql55> explain SELECT* FROM card WHERE suite= 'heart';+----+-------------+-------+------+------------------+------------------+---------+-------+------+--------------------------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |+----+-------------+-------+------+------------------+------------------+---------+-------+------+--------------------------+| 1 | SIMPLE | card | ref | idx_suite_number | idx_suite_number | 50 | const | 21 | Using where; Using index |+----+-------------+-------+------+------------------+------------------+---------+-------+------+--------------------------+右側のカラムだけ使おうとした場合mysql55> explain SELECT * FROM card WHERE number = 1;+----+-------------+-------+-------+---------------+------------------+---------+------+------+--------------------------+| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |+----+-------------+-------+-------+---------------+------------------+---------+------+------+--------------------------+| 1 | SIMPLE | card | index | NULL | idx_suite_number | 51 | NULL | 100 | Using where; Using index |+----+-------------+-------+-------+---------------+------------------+---------+------+------+--------------------------+右側のカラムだけ使おうとしたのはtype: indexでインデックスの全体をスキャンしている。SELECT suite, number, GROUP_CONCAT(seq ORDER BY seq) AS 上からFROM card GROUP BY 1, 2; の結果を⾒ると何となく想像がつくはず。115/144
他にもたとえばコメント数の多いユーザー10⼈を抽出するクエリーSELECT user.user_name,COUNT(*) AScomment_countFROM user JOIN comment ON user.user_name = comment.comment_ownerGROUP BY user.user_nameORDER BY comment_count DESC LIMIT 10commentテーブルの結合のためにidx̲commentowner(comment̲owner)userテーブルのGROUP BYのためにuser̲nameはプライマリーキーなのでこのままでOKORDER BYのcomment̲countは集計関数の結果カラムなのでソートは避けられない5.6以降ならばソート⽤の⼀時インデックスを⾃動で作るかも。-136/144
指標的なものEXPLAINExtra: Using filesortでORDERBYやGROUP BYがインデックスで解決できていない-Extra: Using tempraryでGROUP BYやJOINがインデックスで解決できていない-Extra: Using join bufferでスキャンジョイン-138/144
140.
指標的なものSHOW GLOBAL STATUScreated̲tmp̲%tables.. テンポラリーテーブルが作成された(Extra: Using temporaryな)クエリーでカウントアップされる-handler̲read% .. 実際に⾏をフェッチしているとカウントアップされる-Select̲full̲join, Select̲scan-Sort̲merge̲passes, Sort̲scan-139/144