PreparedStatementの罠。
PreparedStatementを使用し、CHAR項目に対してSELECT/UPDATEなどをする場合気をつけなければいけないこと。
※Java1.5/1.4/1.3+Oracle JDBCドライバ10.2/10.1で再現。
あるテーブル「TABLE1」
COL1|CHAR(3)
COL2|VARCHAR2(3)
「TABLE1」のデータ
COL1|COL2
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
01 |01
012 |012
このとき、以下の結果は正しく帰ってくる。
Statement stmt=conn.createStatement();
1)ResultSet rs1=stmt.executeQuery("SELECT * FROM TABLE1 WHERE COL1='01'");
2)ResultSet rs2=stmt.executeQuery("SELECT * FROM TABLE1 WHERE COL1='012'");
3)int update1=stmt.executeUpdate("UPDATE TABLE1 SET COL2='013' WHERE COL1='01'");
4)int update2=stmt.executeUpdate("UPDATE TABLE1 SET COL2='013' WHERE COL1='012'");
しかし、次のような形だと、1)と3)が0件のヒットまたは更新0件になる。
1)
PreparedStatement pstmt1=conn.prepareStatement("SELECT * FROM TABLE1 WHERE COL1=?");
pstmt1.setString(1,"01");
ResultSet rs1=pstmt1.executeQuery();
2)
PreparedStatement pstmt2=conn.prepareStatement("SELECT * FROM TABLE1 WHERE COL1=?");
pstmt2.setString(1,"012");
ResultSet rs2=pstmt2.executeQuery();
3)
PreparedStatement pstmt3=conn.prepareStatement("UPDATE TABLE1 SET COL2='013' WHERE COL1=?");
pstmt3.setString(1,"01");
int update1=pstmt3.executeUpdate();
4)
PreparedStatement pstmt4=conn.prepareStatement("UPDATE TABLE1 SET COL2='013' WHERE COL1=?");
pstmt4.setString(1,"012");
int update2=pstmt4.executeUpdate();
違いはCHAR項目に対して、どういった値を渡しているか。
1と3の場合、CHAR項目としては「01 」になるが、渡しているStringは「01」のため0件になる。
可変長の値をCHARにつめ、かつPreparedStatementを使用する際には気をつけないといけないらしい。
回避策(1):検索項目をTRIMする※遅い
・PreparedStatement pstmt1=conn.prepareStatement("SELECT * FROM TABLE1 WHERE TRIM(COL1)=?");
回避策(2):検索条件に対し、空白をADDする。
・PreparedStatement pstmt1=conn.prepareStatement("SELECT * FROM TABLE1 WHERE COL1=?");
pstmt1.setString(1,"01 ");//←01半角空白になるように加工する
ResultSet rs1=pstmt1.executeQuery();
OracleでExportコマンドを実行すると、エラーが発生。
環境
Oracle8i(それ以上は不明)
内容
EXP-00008: Oracleエラー 904が発生しました。
ORA-00904: 列名が無効です。
EXP-00008: Oracleエラー 6550が発生しました。
ORA-06550: 行 1、列 29:
PLS-00302: コンポーネント IS_TRIGGER_FIRE_ONCE_INTERNALを宣言してください。
ORA-06550: 行 1、列 8:
PL/SQL: Statement ignored
EXP-00056: Oracleエラー 1403が発生しました。
ORA-01403: データが見つかりません。
EXP-00000: エラーが発生したためエクスポートを終了します。
よくバージョンが違うOracleへExport/Importする場合に発生する事象で、
通常は以下のsqlをSysユーザで実行し、再エクスポートする。
ORACLE_HOME\rdbms\ADMIN以下の、
・catalog.sql
・catproc.sql
・catexp.sql
・utlrp.sq
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
今回はOracleのDBとSQL*Plusのバージョンで、SQL*Plusのバージョンが新しかったのが原因。
同じか下位バージョンでないといけません。
文字化けに困ったらときの参考。
【Microsoft】SHIFT - JIS と Unicode 間の変換問題
http://support.microsoft.com/default.aspx?scid=kb;ja;JP170559
【Oracle】JDBC 8.1.7上でのNLS環境におけるキャラクタの整合性の問題について
http://otndnld.oracle.co.jp/tech/java/htdocs/javanls/javanls817.html
【Java】Java Encoding & Aliases Table
http://www.ingrid.org/java/i18n/encoding/table-j.html
Oracle Database 8i(charset:JAPANESE_JAPAN.JA16SJIS)とoo4oの接続
1.文字化けしないバージョン
○Windows2000/XP+Oracle Client 8.1.6/8.1.7のoo4o
2.文字化けするバージョン
×Windows2000/XP+Oracle Client 9.0/9.1/9.2のoo4o
回避方法1(9.2のみ)
oo4oを使用するexeをキックする前に、環境変数を設定する。
・set NLS_LANG=JAPANESE_JAPAN.JA16SJISTILDE
※他、レジストリに設定する方法等もある。
回避方法2
Export→DBをJAPANESE_JAPAN.JA16SJISTILDEで作成しなおし→Import
3.サポート外のバージョン
×Windows2000/XP+Oracle Client 10.0/10.1/10.2のoo4o
※SQL*PLUSからしてサポート外。