LIKE 句でワイルドカード文字の検索
JAVAでSQLを実行する時、PreparedStatementを使ってます。
これを使ってると文字列連結してSQL文を組み立てるよりもセキュリティー的に安心だしエスケープ文字(シングルクォーテーション、パーセント...etc)も心配しなくていいし普通は問題ないのです。
ただひとつ落とし穴が...。
LIKE句でパーセント記号(%)と下線(_)を文字通りに検索したい時はPreparedStatementを使用しただけではうまくいかないのです。
LIKE句ではパーセント記号と下線はワイルドカードとして機能してしまうんですね。じゃどうするかというと
SELECT NAME FROM SAMPLE WHERE NAME LIKE ? {ESCAPE '!'}
↑のように書くわけです(この場合、ESCAPEの後ろの'!'をエスケープ文字として使います)。
'john_'に続く文字を検索したかったら?に'john!_%'とバインドしてやればいいわけです。
バインド後のSQL文のイメージは
SELECT NAME FROM SAMPLE WHERE NAME LIKE 'john!_%' {ESCAPE '!'}
↑こんな感じ。
'john_lennon'とか'john_lemon'とか'john_entwistle'とかは引っかかりますが
'john-lennon'とか'john lennon'だと引っかかりません。
注意点としてはLIKE句以外では無効でLIKE句のたびに{ESCAPE '!'}を書かないとうまくいきません。
SELECT NAME FROM SAMPLE WHERE NAME LIKE 'john!_%' AND ID = '1' {ESCAPE '!'}
↑だと構文エラーになってしまいます。
条件が複数あるときは
SELECT NAME FROM SAMPLE WHERE NAME LIKE 'john!_%' {ESCAPE '!'} AND ID = '1' AND BANDNAME LIKE '%!_beatles' {ESCAPE '!'}
↑のように書かないといけません。
なんかLIKE句を書くたびに{ESCAPE '!'}を書くのはめんどくさい、定数にすればいいんだけどでもなぁ...
というわけで、文字列にLIKEが入っていれば{ESCAPE '!'}を挿入するメソッドを作ってみました。
private String escape(String sql) { if(sql.toUpperCase().indexOf("LIKE") == -1) { return sql; } StringBuffer sb = new StringBuffer(); for(StringTokenizer i = new StringTokenizer(sql); i.hasMoreTokens();) { String token = i.nextToken(); sb.append(token); sb.append(" "); if(token.equalsIgnoreCase("LIKE")) { sb.append(i.nextToken();); sb.append(" {ESCAPE '!'} "); } } return sb.toString(); }
↑こんな感じ。
簡単な検証しかしてないので動作保障は出来ませんけど...
なんにしてもエスケープ文字はめんどくさい。