さて、クエリにJOINが絡む場合にはファイルソートが必要になるかをどのように判断すればいいだろう。複雑だと思われるかも知れないが、実は判断基準は至ってシンプルである。MySQLは基本的に次の選択肢しかない。
駆動表でソートしてからJOINするか、JOINしてから全体をソートするか。
さらに駆動表のソートでインデックスを利用できるかどうかという選択肢も加わるので、JOINにおいてどこでソートがされるかは、都合3通りのパターンに分けることが出来る。当然ながら駆動表においてインデックス順にソートすることが出来るならばそれが一番効率的であり、駆動表でソートできない場合が最も効率が悪い。以下にそれぞれの場合のEXPLAIN実行結果を紹介しよう。
まずは駆動表においてインデックスを使ってソートする場合。
mysql> EXPLAIN SELECT * FROM CountryLanguage cl JOIN Country c ON cl.CountryCode = c.code -> WHERE cl.CountryCode LIKE 'j%' ORDER BY cl.CountryCode,cl.Language\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: cl type: range possible_keys: PRIMARY key: PRIMARY key_len: 3 ref: NULL rows: 16 Extra: Using where *************************** 2. row *************************** id: 1 select_type: SIMPLE table: c type: eq_ref possible_keys: PRIMARY key: PRIMARY key_len: 3 ref: world.cl.CountryCode rows: 1 Extra: 2 rows in set (0.00 sec)
次に駆動表でファイルソートする場合。Using filesortが出ている。
mysql> EXPLAIN SELECT * FROM CountryLanguage cl JOIN Country c ON cl.CountryCode = c.code -> WHERE cl.CountryCode LIKE 'j%' ORDER BY cl.Percentage\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: cl type: range possible_keys: PRIMARY key: PRIMARY key_len: 3 ref: NULL rows: 16 Extra: Using where; Using filesort *************************** 2. row *************************** id: 1 select_type: SIMPLE table: c type: eq_ref possible_keys: PRIMARY key: PRIMARY key_len: 3 ref: world.cl.CountryCode rows: 1 Extra: 2 rows in set (0.00 sec)
最後にJOINしてからファイルソートする場合。Using temporary; Using filesortが出ているのがポイントである。
mysql> EXPLAIN SELECT * FROM CountryLanguage cl JOIN Country c ON cl.CountryCode = c.code -> WHERE cl.CountryCode LIKE 'j%' ORDER BY c.Name\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: cl type: range possible_keys: PRIMARY key: PRIMARY key_len: 3 ref: NULL rows: 16 Extra: Using where; Using temporary; Using filesort *************************** 2. row *************************** id: 1 select_type: SIMPLE table: c type: eq_ref possible_keys: PRIMARY key: PRIMARY key_len: 3 ref: world.cl.CountryCode rows: 1 Extra: 2 rows in set (0.00 sec)
本エントリでは複数のテーブルが絡むJOINについてのさらなる解説は割愛するが、興味のある方は筆者が過去に書いた次のブログエントリを参照して頂きたいと思う。
漢(オトコ)のコンピュータ道: Using filesort
ORDER BY句でソートを行う場合、何が何でもインデックスを使ったほうが良いか?と言われると、筆者はそのように考えていない。ファイルソートになっているクエリをインデックスを使うようにするということは、当然ながら新たにインデックスを追加することになるからだ。インデックスを追加するとデータサイズが大きくなるし、更新の負荷も上昇してしまう。それに、ファイルソートはそこまで遅くはない。ソートするレコード数が十分に小さければ、ファイルソートのままでも構わないと思う。
問題となるのはORDER BY句がLIMIT句といっしょに用いられる場合であろう。ファイルソートをしてからLIMIT句でレコードを絞り込むと、せっかくソートした結果が無駄になってしまう。特に、膨大なレコード数をテーブルからフェッチし、ソートしてからLIMIT句でほんの少しのレコードに絞り込むというのは、非常に効率が悪い。LIMIT句がある場合、テーブルからインデックス順にレコードをフェッチしているだけであれば、LIMIT句で指定されたレコード数をフェッチし終えた時点でクエリを終了すれば良いので効率的である。