« ウェブサービスのためのMutex - KeyedMutex | メイン | Perl で並列処理 (using マルチプロセス) »

2007年09月28日

DBI::Printf - A Yet Another Prepared Statement

 Java や C++ のような関数のオーバーロードができる言語では、プリペアードステートメントのプレースホルダが型をもつ必要はありません。しかし、Perl のように数値型と文字列型の区別がない言語で最善を期そうとすると、変数をバインドするタイミングで型を意識してコードを書かなければならず面倒です。 (参考: MySQL の高速化プチBK)

 だったら、printf のように、プリペアードステートメントのプレースホルダで型を指定できればいいのに、と、もともとは Twitter でつぶやいたネタなのですが、SQLステートメントをキーとしてキャッシュにデータを保存したいといった事情もあって、実際に作ってしまいました。

Kazuho Oku / DBIx-Printf - search.cpan.org
DBI-Printf-0.01.tar.gz

 使い方は至って単純。$dbh->printf を呼び出せば、エスケープされた SQL 文が返ってきます。

use DBIx::Printf;
...
my $sql = $dbh->printf(
    'select * from t where str=%s and int=%d and float=%f',
    '文字列',
    1,
    1.1e1,
) or die $dbh->errstr;

 これで、自然な形で型つきのプリペアードステートメントを実現することができました。1万人くらい同じアイデアを思いついた人がいそうですが、いいものはいいよね、ということで (CPAN で重複があったら教えていただけると助かります)。

補足: MySQL におけるプリペアードステートメントの扱いについて
 パフォーマンスの点から言うと一般的には、サーバサイドプリペアードステートメントを備えている RDBMS においては、自前の (クライアントサイドの) プリペアードステートメント機能を使うのではなく、RDBMS のそれを使うべきです。これは、SQL の構文解析結果や実行プラン等をサーバ側で使い回すことで、より効率的な処理が可能になるためです。MySQL においても 4.1 以降では、サーバサイドプリペアードステートメントに対応しています。しかし、現段階での MySQL のサーバサイドプリペアードステートメントには様々な制限があり、なかなかパフォーマンスを稼ぐのが難しそうです。個人的には、ステートメントキャッシュがないというあたりで使う気が失せました。このあたりの細かな事情については、MySQL Performance Blog » MySQL Prepared Statements が参考になると思います。

17:03追記:
 DBI::Printf のソースコードを CodeRepos に置いてみましたので、興味ある方はぜひ勝手に改良していただければと思います。yappo++

2007年9月30日追記:
 CPAN にアップロードしたところ、「The DBI::* namespace has always been kept reserved for module which are part of the DBI distribution.」とのことだったので、DBIx ネームスペースへ移動しました。最新版は Kazuho Oku / DBIx-Printf - search.cpan.org からダウンロード可能です。

投稿者 kazuho : 2007年09月28日 10:10 このエントリーを含むはてなブックマーク このエントリーを含むはてなブックマーク

トラックバック

このエントリーのトラックバックURL:
http://labs.cybozu.co.jp/cgi-bin/mt-admin/mt-tbp.cgi/1554

このリストは、次のエントリーを参照しています: DBI::Printf - A Yet Another Prepared Statement:

» [perl][db] DBI で printf オレオレ流 from daily dayflower
Kazuho@Cybozu Labs: DBI::Printf - A Yet Another Prepared Statement を読んでおもしろ,... [続きを読む]

トラックバック時刻: 2007年09月28日 18:30

» [Perl]Scalar 変数の型を取得する from odz buffer
ref:Kazuho@Cybozu Labs: DBI::Printf - A Yet Another Prepared Statement ref:4... [続きを読む]

トラックバック時刻: 2007年09月30日 23:04

コメント

こんにちは。

プリペアードステートメントというと,(お書きになっているように)構文解析済みSQLステートメントオブジェクトを一度作っておけば,それに何回も違うparametersでexecuteできて速いよ,というイメージがあるので,これをプリペアードステートメントと呼ぶことに抵抗感があるのですが。構文解析をしろということではないのですが,このアプローチだとパラメータを変える度に都度都度SQLを発行するわけですよね。

ソースをちら見しただけなので誤読があったらごめんなさい。

投稿者 dayflower : 2007年09月28日 16:23

コメントありがとうございます。

> このアプローチだとパラメータを変える度に都度都度SQLを発行するわけですよね。

はい。DBI のプリペアードステートメントのラッパーのようにすることでサーバーサイドプリペアードステートメントに対応させることもできるとは思いますが、本文に書いたような事情により、自分で対応作業をする気はありません。

クライアントサイドで毎回 SQL 文を構築するのにプリペアードとはなにごとぞ、というのはおっしゃるとおりだと思いますが、MySQL ではそのようなものを client-side prepared statement と呼んでいるようです (perl の DBI もデフォルトでは client-side prepared statement 方式だったと思います)。

投稿者 kazuho : 2007年09月28日 16:43