sprintfに配列を渡す
普通に配列を指定したら、展開してくれないんですね……。
調べたら、配列を引数にしたいときは別関数「vsprintf」を使うらしい(printfの場合も同様にvprintfを使う)。なんかPHPってこういう細かい用途で関数が分かれてたりするんで、初学者にはわかりにくいんだけど、バージョンアップ時、既存の関数の機能を拡張するのではなく新しい関数を作る主義なんだろうか?
$param = array(1, 2);
$str = vsprintf('SELECT * FROM TABLE1 WHERE ID1 = %d AND ID2 = %d', $param);
なんで配列を渡したかったかっていうと、PDOStatement::executeみたいにプレースホルダに値を埋め込みたかったんだよね。そりゃ、完全にSQLを組み立ててからならexecuteでいいだろうけど、WHERE句の個々の条件など、部品としてのSQLに値を埋め込みたかったんで。
要はサイト検索時、条件の種別ごとにSQL部品を用意しておき(プレースホルダの数が違う)、ユーザが指定した値を埋め込んで、最後に全部連結、ということをやりたかったわけ。
$condition = array(
'sitetype'=>'S.SITE_TYPE = %d'
,'cate'=>'S.ID IN (SELECT DISTINCT SITE_ID FROM D_CATE WHERE CATE_ID1 = %d AND CATE_ID2 = %d)'
);
$where1 = vsprintf($condition['sitetype'], $array1);
$where2 = vsprintf($condition['cate'], $array2);
sprintfを使えないとわかって、最初は諦めて自前で処理をしようと思ったけど、SQLインジェクション対策としてPDOStatement::executeが推奨されていることを考えあわせると、いかにもまずい。で、PHPではどうするのがセオリーなんだろうとネットを検索したら、こういうオチでした。
ところで、SQLiteではEXISTSを使えないと思い込んでいたけど、あらためてリファレンスを見たらしっかり書いてありますね。最初に見落としたかな? ってことは条件に合致するサイトを引っ張ってくるときに上記のようなIN句を考えていたけど、EXISTSで判定すればいいかな。
でも問題はパフォーマンス。EXISTSの場合、サブクエリから実行される通常のサブクエリと違って、まず外部クエリから実行されるんだよね? IN句の値の羅列はパフォーマンスが悪いにしても、実行に先立ってまず全サブクエリを一回ずつ実行、その後外部クエリを実行――となるように思うけど。翻ってEXISTSは外部クエリ→EXISTSを擁するサブクエリ、と毎回実行されるんだよね? この認識、間違ってるかなー。
とりあえず安直にINで書いていたサブクエリをEXISTSで書き直して、実際にシステムができあがってからパフォーマンステストの結果次第で考えるか……。
2010/09/07 14:08:10
最終更新:2010年09月07日 14:08