【問題提起】
検索エンジンなどでよく採用されているように、複数のキーワードをスペースで区切って与え、それをストアドプロシージャで検索する方法について考えてみた。
1.プログラム側でSQLの検索部分のみを組み立てて、一つのパラメータでストアドプロシージャに渡してしまう方法
2.あらかじめキーワード用に20個ぐらいのパラメータをストアドプロシージャに用意しておき、複数のキーワードを一つずつ、そのパラメータに指定してストアドプロシージャに渡す方法
3.入力されたキーワードの塊(空白で区切られた複数のキーワード)を一つのパラメータでストアドプロシージャに渡す方法
どの方法が良いのだろうか? 以下に考察してみた。
【解決方法の考察】
▼1.の場合だと、文字をエスケープする問題が発生する。QUOTENAME関数を使ってエスケープしてしまえば良いが、この関数はどうして一気に全てのエスケープをしてくれるモードがないんだろうか? 結局、単一引用符 (')、左または右の角かっこ ([ ])、二重引用符 (") の全てに関して、エスケープ処理しなければならない。
(参考)
QUOTENAME (Transact-SQL)
http://msdn2.microsoft.com/ja-jp/library/ms176114.aspx
▼2.だとエスケープ処理を気にする必要がない。パラメータをSQL文に適用する際に、自動的に行ってくれるからである。ただし、like文におけるパラメータである場合は、%などがエスケープされないため、自前でエスケープしておく必要がある。この%などのエスケープ処理はプログラム側でもできるが、ストアドプロシージャ内で行った方が良いであろう。プログラム側で行う場合、この処理を記述するのを忘れる心配もあるし、オブジェクト指向的な考え方からも、ストアドプロシージャにカプセル化してしまう方が良いと思うからである。
ストアドプロシージャ内でのエスケープは、以下を行っておけば良い。
SET @param1 = Replace(@param1, '[', '[[]')
SET @param1 = Replace(@param1, '%', '[%]')
SET @param1 = Replace(@param1, '_', '[_]')
そして、
SET @param1 = '%' + @param1 + '%'
などとして完成。
SQL文的には、
select hoge from table where hoge like @parame1 or hoge like case when @param2 != '' then @param2 else hoge end or hoge = ..........
のように書いておけば良いだろう。上記は、少なくとも1つはキーワードが指定されていることを前提としている。
また、sp_executesqlを使っても良い。指定された数のキーワードからのみなるlike文を動的に作成し、それをsp_executesqlで実行する。sp_executesqlで使用するパラメータは20個全て指定しておく。SQL文中で使用されていない余分なパラメータは無視されるようで、問題なく動く。
DECLARE @sqlwk nvarchar(500)
SET @sqlwk = 'select test1 from test where test1 like @param1 or '
IF @param2 != ''
SET @sqlwk = @sqlwk + 'test1 like @param2 or '
IF @param3 != ''
SET @sqlwk = @sqlwk + 'test1 like @param3 or '
.
.
.
.
.
EXEC sp_executesql @sqlwk, N'@param1 nvarchar(100), @param2 nvarchar(100), ..... , @param20 nvarchar(100)', @param1, @param2, ..... , @param20
▼3.の場合は、基本的に2と同じである。複数のキーワードを一つずつにばらすことを、プログラム側で行うか、ストアドプロシージャ側で行うかの違いである。パラメータ数に20という制約があるのであれば、プログラム側で20を超えないようにチェックした方がよいので(無駄にデータベースに問い合わせる必要がないため)、プログラム側でばらすのが普通であろう。
また、ストアドプロシージャ側でばらすのは、できなくはないと思うが、文字列操作などの関数が少ないため、面倒であろう。
【結論】
総合的にみて、やはり2だと思われる。キーワードは通常は20も用意しておけば十分であり、この20が固定されること以外はベストだからである。1はプログラム側でlike文を作成しなければならないし、3なら2でしょって感じ。
というわけで、これから開発にかかります。え~、そうじゃないでしょ!ってお叱りをいただけるなら、是非お願いします。
【おまけ】
ストアドプロシージャを使わずに、プログラム側で動的にパラメタライズドクエリを組み立てて発行するという方法もある。ただ、今回は、検索条件が他にもたくさんあるため、ストアドプロシージャですっきりと書きたかった。それに、あくまでストアドプロシージャにこだわりたい。それは、ストアドプロシージャが好きだから(笑)