読者です 読者をやめる 読者になる 読者になる

setString で CHAR 型の値がとれない!!当たり前っ!!

表題の内容で一悶着あったのでメモがてらに書いておきます。
まず、以下のようなテーブルがありました。あっ、ちなみに DB は Oracle です。

CREATE TABLE HOGE(
    COL CHAR(8)
)

データは以下のような感じでした。

SQL> SELECT * FROM HOGE

COL
---------------
AAAA____
*アンダースコア(_)は半角空白です。

で、 JDBC を使って以下のように SELECT 文を発行したところ

String sql = "SELECT * FROM HOGE WHERE COL = ?";
//中略・・・
statement.setString(1,"AAAA");
//中略・・・

値がとれないらしいです。っていうか、当たり前ですやん。後ろのスペースがついてないのに取れるわけないやん。それで取れたら逆にビックリします。しかし、SQL*Plus では取れるとの事。そりゃそうや。それは取れるよ。ちなみに SQL*Plus ではこんな感じに書いありました。

SQL> SELECT * FROM COL = 'AAAA';

で、以下からなぜ取れないかを書いていきます。

まずはじめに、SQL*Plus から流した SQL 文と JDBC で流した SQL 文は同じようで違う SQL 文です。SQL*Plus のほうは文字列埋め込みの SQL 文で、 JDBC はパラメーターとして値を渡す SQL 文です。
文字列に直接埋め込まれた値は文字列リテラルとして扱われます。文字列リテラルの比較の場合は空白が自動でパディングされます。なので SQL*Plus では条件に一致します。しかし、STRING でパラメーターとして渡された値は VARCHAR として扱われます。この場合は文字列リテラルとは違いレングスを正確に扱うので空白はパディングされません。そのため条件にも一致しません。通常の動きです。そのため、条件を一致させようと思うと値自身に空白をパディングして渡すか、SQLの関数で空白を埋めるかする必要があります。

しかし、そもそもとして DB の CHAR 型は長さを固定する必要があるから使用するものです。長さが固定であることがそのデータのメタデータとして定義されているのですから、空白をパディングしなければ取れないというのは当たり前のことです。それをもってして空白をパディングしなくても取得したいというのは、CHAR 型をつかっていること自体を否定しているのと同等です。どうやって取得するのかを考える前に CHAR 型を使うかどうかを考えたほうがいいでしょう。
便利だからといって、意味自体を捻じ曲げてしまうのはナンセンスです。