ケースによりますが、最近のプロジェクトだと、searchメソッドじゃ足りないことが多かった(50%くらい)ので、SQL::Abstract::Moreをクエリービルダーに使えるようにしました。
Teng::Plugin::SearchBySQLAbstractMore
リリースしたらblog書こうと思ってたら、リリースしては改変を繰り返してしまい、0.06っていう…どんだけバージョン上げてんだかorz
気を取りなおして…。
Tengのクエリービルダーとして、SQL::Abstract::Moreを使えるようになります。
これの利点としては、
- group by 可能
- having とかも書ける
- (x = 1 or y = 2) のような別カラムの or が書きやすい
- 複雑な join も書ける
- SQL::Abstractに慣れてる人は書きやすい & SQL::Abstractを素で使うよりは全然楽
という、感じです。
デメリットとしては、
Rate sql_abstract_more sql_abstract sql_maker sql_abstract_more 2692/s -- -21% -64% sql_abstract 3422/s 27% -- -55% sql_maker 7566/s 181% 121% --
ベンチマークのコードは、https://gist.github.com/1566118。
とはいえ、実際問題としてはこれがボトルネックになることはないでしょう。
Teng::Plugin::SearchBySQLAbstractMore には、以下の5つのプラグインがあります(最初3つだったんだけどな...)。
- Teng::Plugin::SearchBySQLAbstractMore
- Teng::Plugin::SearchBySQLAbstractMore::Pager
- Teng::Plugin::SearchBySQLAbstractMore::Pager::MySQLFoundRows
- Teng::Plugin::SearchBySQLAbstractMore::Pager::Count
- Teng::Plugin::SearchBySQLAbstractMore::Pager::CountOrMySQLFoundRows
で、普通にload_pluginすれば、それぞれ、
- search_by_sql_abstrat_more
- search_by_sql_abstrat_more_with_pager
- search_by_sql_abstrat_more_with_pager (CALC_FOUND_ROWS を使うやつ)
- search_by_sql_abstrat_more_with_pager (count(*) を使うやつ -- group by や having を使ってると微妙かも知れない)
- search_by_sql_abstrat_more_with_pager (count(*) or CALC_FOUND_ROWS(group by or havingがある時のみ)を使う)
が生えます。Teng の search と一応互換性があるはず。Tengのsearch関連のテストをいくつか通しています。
Tengと同様の使い方で引数を渡す場合は、Tengの引数 -> SQL::Abstract::Moreに渡せるように変換しているのですが、
SQL::Abstract::More と同様の引数も取ることが出来ます。
その場合は、第一引数を table名の代わりにハッシュリファレンスを渡します。
$teng->search({ -columns => [qw/col1 col2 col3/], -from => 'table', -where => { col1 => {'>' => "1"}, }, });
この渡し方であれば、SQL::Abstract::Moreと全く同じ使い方です。
SQL::Abstract::Moreの使い方については、SQLを組み立てるものという記事に若干書いてます。
search、search_with_pager というメソッド名で使いたい場合は、
YourClass->load_plugin('Teng::Plugin::SearchBySQLAbstractMore', { alias => 'search_by_sql_abstract_more' => 'search', }); YourClass->load_plugin('Teng::Plugin::SearchBySQLAbstractMore::Pager', { alias => 'search_by_sql_abstract_more_with_pager' => 'search_with_pager', }); YourClass->install_sql_abstract_more(pager => 'Pager'); # or pager => 'Pager::MySQLFoundRows'
のようにすれば、良いかと思います。メンドクサイよっていう人は、
use Teng::Plugin::SearchBySQLAbstractMore; YourClass->install_sql_abstract_more(alias => 'search', pager => 'Pager'); # or pager => 'Pager::MySQLFoundRows' / 'Pager::Count'
とすると、
- YourClass に search を生やす
- YourClass に search_with_pager を生やす
※aliasって、ドキュメントに書かれてないので、ずっと残る物なのかは知らないです。
Teng::search を置き換えることもできます。
use Teng::Plugin::SearchBySQLAbstractMore; YourClass->install_sql_abstract_more(pager => 'Pager', replace => 1);
とすると、
- Teng の search メソッドを search_by_sql_abstract_more で置き換え
- search_with_pager を生やす
Teng::search を置き換えるのはあんまりよろしくない気がするので、気になる方は使わない方が良いと思います。
ていうか、いらない気がしますね。無くすかも知れません。
DBにOracleを使ってるんだよ、とか言う場合は、
YourClass->sql_abstract_more_new_option(sql_dialect => 'Oracle');
のように、渡すことが出来ます(Oracleないので確かめてないけど)。このnew_optionに渡した引数は、そのままSQL::Abstract::Moreのnew時に渡されます。
詳しくはそちらのドキュメントを読んで頂ければと思います。
Tengのsearchだと表現できないけど、SQLの条件を文字列で組み立てるのはちょっと…
っていう方は使ってみて頂ければと思います。
追記: DBIx::QueryLogを使う場合、以下のコードを書いておいたほうが良いです。
%DBIx::QueryLog::SKIP_PKG_MAP = ( 'Teng::Plugin::SearchBySQLAbstractMore' => 1, 'Teng::Plugin::SearchBySQLAbstractMore::Pager' => 1, # ここは使うPagerプラグインを適当に %DBIx::QueryLog::SKIP_PKG_MAP );
ここに書かれたpackageは、無視して、その先までたどってログに行数を出してくれます。
xaicron さんの以下の記事に書かれています。
http://blog.livedoor.jp/xaicron/archives/51554520.html