色々な検索方法2 #06
こんにちわ!新幹線でこの原稿を書いているnekokakです!
六日目はDBIx::SkinnyでのDB検索の細かい使い方についての続きです。
search_by_sql
このsearch_by_sqlメソッドはDBIx::Skinnyを開発するきっかけとなったメソッドの一つで、
好きに生のSQLを利用してDB検索を行う事が可能です。
$db->search_by_sql( q{SELECT * FROM user WHERE name = ?}, ['nekokak'], 'user' );
この例ではsearch_by_sqlを使うほどでもありませんが、
ORMでは表現することが難しい複雑なSQLでもsearch_by_sqlを使えば、
他のsearchメソッドと同じように結果をイテレータにしてくれたり、
レコードをRowオブジェクトにする事が可能です。
search_by_sqlの引数では以下が指定できます。
第一引数に発行したい生SQL
第二引数にbindさせる値のarrayref
第三引数に結果をどのテーブルをベースにrowオブジェクトにするかの指定
となります。
第三引数はオプショナルで指定しなくてもよいです。
search_by_sqlを使う上での注意点は好きにSQLをハードコードする事が出るため
値をハードコードしてしまう人が居るかもしれないという事でしょうか。
例えば、
$db->search_by_sql( qq{SELECT * FROM user WHERE name = $name}, [], 'user' );
このようにプレスホルダーを使わずにSQLの生成を行っている箇所で直接
変数を展開しないでください!
上記の例のような書き方をしていると、SQLインジェクションが発生する可能性が高くなります。
もし上記の例の$nameがCGIなどでユーザが入力した値をそのまま使ってしまっていると悲惨な事になるでしょう。
search_by_sqlメソッドを上手につかえば、普段はsearchやsingleで情報をやり取りし、
複雑なクエリを発行しなければならない箇所でSQLを直接書くことで、
その処理が何をやっているのかが分かりやすくなるのではないでしょうか。
search_named
search_namedメソッドはsearch_by_sqlにいくつかの機能を追加したメソッドとなります。
search_by_sqlでは生のSQLを書くときに、
$db->search_by_sql( q{SELECT * FROM user WHERE name = ?}, ['nekokak'], 'user' );
このように?でプレスホルダーを指定し、その順番を崩さないように、bindの値を指定する必要があります。
例えば、
$db->search_by_sql( q{SELECT * FROM user WHERE id < ? AND name = ?}, ['nekokak', 10], 'user' );
これはSQLは正常に発行されDBがエラーを発生させないとしても、
期待した値をとれませんよね?
?でのプレスホルダーはbindさせる値の順番に気を使う必要があります。
単純なSQLだったり、bindさせる値が少ない場合はまぁいいのですが、
複雑なSQLでbindさせる値が多くなると、順番を考えるのが大変になってきます。
そこでsearch_namedメソッドを使用することで、bindさせる値の順番を考えなくてよくする事が出来ます。
$db->search_named( q{SELECT * FROM user WHERE id < :id AND name = :name}, {id => 10, name => 'nekokak'}, [], 'user' );
第一引数に生のSQLを書きます。
ここでsearch_by_sqlの時にプレスホルダーで?を使用していましたが、
:id/:nameなどの名前を指定します。名前はコロンをプレフィックスとしてつけます。
第二引数にbindさせる値をhashrefの形式で書きます。
こうすることで、内部ではSQLのnamedプレスホルダーの部分とhashrefで渡した値を
つきあわせて自動で:id/:nameを?に変換し、bindの値の順番を正確な位置に保持してくれます。
また、同じnamedプレスホルダーを指定する事も可能で、複数同じnamedプレスホルダーを指定しても
正しくbindする値の順番を決めてくれます。
第三引数では、第一引数で書いたSQLをsprintfでお着替えしてくれます。
$db->search_named( q{SELECT * FROM user WHERE id < :id AND name = :name %s}, {id => 10, name => 'nekokak'}, ['AND name IS NOT NULL'], 'user' );
この例ではあまり使いどころが思い浮かばないかもしれませんが、
こういう事も出来るというのを覚えておくといい事があるかもしれません。
明日は検索系メソッド最後のresultsetを紹介してみようと思います。
have a nice skinny days!:)