2017 03 21

SQL Server における空白の扱い

俺が扱うデータベースはだいたい Oracle だが、たまに SQL Server の時もある。 なので SQL Server 相手についつい Oracle のつもりでやってしまって嵌る時がある。 最近嵌ったのが空白の扱い。

select * from TBL where COL = 'ABC ' ; select * from TBL where COL = 'ABC' ;

SQL Server って、この2つのSQLが同じ結果になるんだね。 検索条件の末尾の空白を無視し、検索結果でも末尾の空白を無視し、結果としてCOL列の値が "ABC" のレコードと "ABC" の後ろに空白だけが付いているレコードが全て返ってくる。 空白の有無だけじゃなく、空白の数も関係無し。

ところが末尾の空白に意味があると察せられるようなSQLだと、末尾の空白の有無を区別してくれる。

例えば

select * from TBL where COL like 'ABC% ' ;

とすると、末尾に空白があるレコードだけが返ってくる。

Oracle ではそんなことはない。 条件として指定した文字列と一致するレコードのみが返ってくるので、最初の例の2つのSQLの実行結果は被らない。

Oracle で同じ結果を得たい場合はこんな感じになる。

select * from TBL where regexp_like( COL, '^ABC[ ]*' ) ;

SQL Server は何故こんな仕様になってるんだろう。 自分が明示的に指定したものだけがヒットする方が安心できると思うのだが。 俺が Oracle に慣れてるからピンとこないだけで、世の中的にはこれが便利だったりするのだろうか。

さすがに前に着く空白は区別してくれる。 次の2つのSQLの実行結果が被ることはない。

select * from TBL where COL = ' ABC' ; select * from TBL where COL = 'ABC' ;

んが、これがJDBC経由の場合はまた違った結果になる。

エッセンスだけ抜き出して、例えば次のような場合。

String sql = "select * from TBL where COL = ?"; // (1) PreparedStatement st = conn.prepareStatement( sql ); st.setString( 1, 'AAA' ); // (2) ResultSet rs = st.executeQuery(); while( rs.next() ) { System.out.print( rs.getString( "COL" ) ); }

まず(1)でプレースホルダーを含むSQLを定義し、これに(2)で値を設定する。 このとき、(2)を次のどれにしても同じ結果になる。

st.setString( 1, 'ABC' ); // 空白無し st.setString( 1, 'ABC ' ); // 後に空白 st.setString( 1, ' ABC' ); // 前に空白 st.setString( 1, ' ABC ' ); // 前後に空白

前後の空白全て無視はさすがに駄目だろうと思うのだがどうよ。

繰り返しになるが、Oracle ではそんなことはない。 上の4つは全て異なる結果が返ってくる。

ああ、いや、どのパターンにも該当データ無しの場合は、結果が無いという結果で被るのだが…って、一々言うことでも無いか。

試したのは、データベースが SQL Server 2012 で、JDBC driver もこれに同梱のもの。 その後のバージョンだとまた違ったりするのかな。