ゆるこあプログラミング

新卒社員がプログラミングとたたかう

クエリを考える際の範囲条件と複数等価条件の違い

こんにちは,coreです

indexを使って,テーブルの最適化を行なっているのですが
どうもEXPLAIN結果を見て,「using index」と出ていると

それだけで満足してしまいます_(┐「ε:)_

それではよくない!
ということで,曖昧になりがちな複合インデックスについて.

エントリーのタイトル,自分でもどうつけようか迷ったんですが
とりあえず詰まったので勉強しながら書いていきます…

今回の場面設定
  • テーブルの最適化
  • SELECTで範囲検索を行いたい
  • explainの結果から分からない状況
  • テーブル定義は次の通り
createdb user_data(
    user_id         int unsigned
    age             int unsigned     #20-80歳
    sex             varchar(2)       #M,F
    updatetime      int unsigned     #unixtime
)type=InnoDB;


で,じゃあ今回は上のテーブルから
20代の女の子,テーブル登録が2013年8月に当てはまる行をSELECTします.


その前に

indexはどうやってはりましょうね

とりあえず,user_idは恐らくユーザ固有のもので
他のテーブルにもuser_idがあって,結びつくはずなので

auto_incrementにしているわけではありませんが
user_idはprimary keyにします

mysql> alter table user_data add primary key(user_id);


それから,セカンダリインデックスについては

今回はSELECT文の,WHERE句以降に
user_id以外の全てのカラムが入ってくるので
複合インデックスになることは大体察しがつきます.

ではどういう順番にインデックスを貼ればいいのでしょうか

コメントアウトにあるように,
実際の想定されるデータ値から考えてみます.
---
age -> 20歳から80歳(つまり60通り)
sex -> 女性か男性(2通り)
updatetime -> unixtime
---

あれ.
えーっと,ageとsexについては値が何通りなのか想像出来ますが
updatetimeについては,決め打ちの値が出せません.

このテーブルに登録されている日時は
「いつが最古で,そしていつまでの値が入っているのか」を
明確化していないからですね.


まぁ,なのでとりあえず
「たくさんある」認識で進めていきます.


複合インデックスはindex i1(A,B,C)としたときに
Aの値をソートし,それにあわせてB,Cをおいた状態でindexが作成されます.

だからそれがごちゃごちゃにならないよう
パターンが少ないものからindexをはっていくと 良いような気がします

mysql> alter table user_data add index i1(sex, age, updatetime);


で SELECT文です.
とりあえず素直に書いてみました.

mysql> SELECT user_id from user_data
     -> WHERE sex = 'F'
     -> AND age BETWEEN 20 AND 29
     -> AND updatetime BETWEEN 1375282800 AND 1377961199;

ところで
MySQLは,範囲条件が2つある場合
その片方でしか綺麗に取ってくることができません.

なので,少し格好悪いかたちになりますが
ageは全てIN句で指定します.

mysql> SELECT user_id from user_data
     -> WHERE sex = 'F'
     -> AND age IN(20,21,22,23,24,25,26,27,28,29)
     -> AND updatetime BETWEEN 1375282800 AND 1377961199;


先ほどあげたクエリと,このクエリ
EXPLAINでみたときに,type=rangeと表示され
ともに範囲クエリであるように思えます.

ただ前者は,カラムageにおいて「値の範囲」を条件とし
後者は「値の等価」を条件としてます.

範囲条件の場合,MySQLはindexの他の列を考慮しませんが
等価条件の場合,そういった制限がありません.


ついつい忘れがちです.


IN句で書ききれないときは,

テーブル設計を見なおしたり
検索条件そのものの方針が誤っていたり

…っていう可能性が
高いのではないでしょうか.