Practice of Programming

プログラム とか Linuxとかの話題

Teng::Plugin::SearchBySQLAbstractMore リリース

ケースによりますが、最近のプロジェクトだと、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を素で使うよりは全然楽

という、感じです。
デメリットとしては、

  • クエリが整形されてないのでデバッグ時に見づらい
  • SQL::Makerより2倍以上遅い
                    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'

とすると、

  1. YourClass に search を生やす
  2. YourClass に search_with_pager を生やす

※aliasって、ドキュメントに書かれてないので、ずっと残る物なのかは知らないです。


Teng::search を置き換えることもできます。

 use Teng::Plugin::SearchBySQLAbstractMore;
 YourClass->install_sql_abstract_more(pager => 'Pager', replace => 1);

とすると、

  1. Teng の search メソッドを search_by_sql_abstract_more で置き換え
  2. 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