DBIx::Simple と SQL::Abstract::Limit

なんちゃってマッパ DBIx::Simple

DBIC などの O/R マッパを使うほどじゃないけど DB 操作で楽したい,という場合には tomi さんも御推薦DBIx::Simple があります。tomi さんもお書きのように結構致命的なバグ(その1その2)が放置されているのが悲しいところですが。

DBIx::Simple を生の状態で使ってもそれほど有難味がないですが,周辺モジュールがインストールされていると透過的に使ってくれるので格段に便利になります。たとえば SQL::Abstract でクエリを簡単に書くことができますし,クエリ結果をテーブルにしたい場合 DBIx::XHTML_Table を使うと楽です。

さらに,DBIx::Simple::OO という拡張モジュールを別途インストールするとクエリ結果を Object::Accessor ベースなオブジェクトにしてくれます。

use DBIx::Simple;
use DBIx::Simple::OO;

my $db = DBIx::Simple->connect('dbi:SQLite:sample.db')
    or die;

my $result = $db->select(
    't_bookmark',          # table name
    '*',                   # columns
    {                      # WHERE clause
        available => 1,
    },
    [                      # ORDER BY clause
        'ctime',
    ]
);

# Using DBIS::OO
foreach my $record ($result->objects) {
    print
        "URL: ",     $record->URL,     " ",
        "Comment: ", $record->comment,
        "\n";
}

# Using DBIx::XHTML_Table
print $result->html;

リレーションを使わなくてよくって SELECT, DELETE が使えればいいやって場合であれば*1これで充分なのがおわかりいただけるかと。

ページングがしたい→ SQL::Abstract::Limit

上記のように DBIx::Simple + SQL::Abstract で直感的に操作ができるのですが,検索結果をページングしたい時に LIMIT 句を指定する方法がありません。

SQL::Abstract はあくまで SQL 文とプレースホルダを構築するだけなので,別々に使えば無理矢理指定することが可能です。

my ($stmt, @bind) = SQL::Abstract->new->select( ... );

$stmt .= ' LIMIT ? OFFSET ?';
push(@bind, 30, 0);

my $result = $db->query($stmt, @bind);

ですがまどろっこしいですし,何を隠そう LIMIT 句は標準 SQL の範疇に入らないので*2データベースによって書き方が異なります。

個人用にラッパでも書くかなぁと思っていましたが,さすが CPAN,とっくにそのようなものがありました。それが SQL::Abstract::Limit です*3。これは先ほど書いた方言を吸収してくれますし SQL::Abstract の上位互換として働きます。

use SQL::Abstract::Limit;

# DBIx::Simple->abstract は lvalue になれる
$db->abstract
    = SQL::Abstract::Limit->new( limit_dialect => $db->dbh );

my $result = $db->select(
    't_bookmark',
    '*',
    {                      # WHERE clause
        available => 1,
    },
    [                      # ORDER BY clause
        'id',
    ],
    30,                    # LIMIT
    0                      # OFFSET
);

limit_dialect で LIMIT まわりの方言を指定します。dbh を与えると JDBC 等特殊な物でなければ自動判別してくれます。

*1:もちろん UPDATE や INSERT も簡潔に書けるのですが,O/R マッパほどオブジェクト透過的でもないので

*2:ウェブ上の SQL92, 99 あたりの情報を探してみましたがやはりなさそうでした。このあたりで入っているというリソースがあれば教えてください

*3:なにげに DBIC − 具体的には DBIx::Class::Storage::DBI でも使われているモジュールです