A Day In The Life

とあるプログラマの備忘録

LIKE 句でワイルドカード文字の検索

JAVASQLを実行する時、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();
}

↑こんな感じ。

簡単な検証しかしてないので動作保障は出来ませんけど...

なんにしてもエスケープ文字はめんどくさい。