- まず検索結果を全部得る
- 件数を数えたいテーブル(この例の場合はnewcity)のユニークな値(ほとんどの場合はPrimaryKeyになってるフィールド、この場合はid)をキーとして多次元配列に格納する
- 関連しているテーブルは↑の配列の子配列?として格納する
- 件数を数える
- めでたくページング完了
というような感じでしょうか。ポイントは多次元配列に格納する部分で、いってみればこれはRDBの正規化?されたデータを元?の親子関係のようなデータ表現?に直す作業という感じなのでしょうか?(詳しいことはよくわかりませんが…)
とにかくこうした形の配列にすることで、ページングできるわけです。ですが検索結果の件数がある程度以上になると全件取ってきてその中から10件とか表示するというやり方では間に合わなくなってきます。そこでSQLのLIMIT句を使おうということになりました。(この時点ではSQLiteを使用していましたが、後にMySQLになりました。LIMITに関しては多少書き方が違うだけでどちらもほぼ同じです。)
LIMIT句とは結果データの一部分を取ってくるためのSQLの句?です。例えば…
SELECT * FROM some_table WHERE some_condition LIMIT(10)
※注:上のLIMITの書き方はSQLiteですね [2007.11.13追記]
などとすればWHERE句までの検索結果が何件だろうと(まあ10件以上を想定していますが…)LIMIT(10)によって10件に絞られます。またoffsetを設定すればデータをどの位置から取ってくるのか?を指定できます。
LIMIT(30, 10); //LIMIT(offset, limit)
LIMIT(10, 30); //LIMIT(limit, offset)
※訂正:MySQLの書式とSQLiteの書式がごちゃ混ぜになってました(--;
括弧を使うのはSQLiteの書き方でしかもパラメータの順番はlimit, offsetのようになります。MySQLでは括弧は書かずにパラメータの順番はoffset, limitになります。というわけで前に書いてあった(打ち消し線)のコードはどちらのDBでも動作しません(TT
などと書けばWHERE句まででヒットした結果の30件目から10件のデータ、という風になります。
簡単!これはイイ! のですが、ページングを実装するためには総件数(LIMITで制限される前の件数)が必要になってくるため、予め
SELECT COUNT(*) FROM some_table WHERE some_condition;
のようにとりあえず全件数だけを取得する必要があります。そのため前述のページング方法よりはSQLを1回多く発行しなければなりません。まあそれでも数百件取ってくるよりは全然効率がいいわけで、実際計ってみても歴然とした差がでてきます。
さらにMySQL限定ですが便利な関数があってこれを使うと上記のCOUNT関数を使うよりも速いというベンチマーク結果もあるようです。とりあえず今回はこの関数については詳しく述べません。
というわけでLIMITの使い方がわかったところで実際に実装してみます。
SELECT pref.name pname, n.id nid, o.id oid, n.city nc, o.city oc, yomi, gdate FROM newcity n, oldcity o, joined j WHERE n.id = j.newcity_id AND o.id = j.oldcity_id AND pref.id = n.pref_id AND (検索条件 ※n.city like '%函館市%' など…) LIMIT 0, 10;
あれ???なんか変だな?なんか短くね?
というわけで見事に失敗するわけです。しかもCOUNTの値もこれじゃダメダメだし(--;
そして旅に出る〜♪
ラベル:PHP