Fight the Future

Java言語とJVM、そしてJavaエコシステム全般にまつわること

OracleでSJISのデータベースの文字列カラムにUnicodeの絵文字をJavaで格納するには

SELECT VALUE FROM NLS_DATABASE_PARAMETERS WHERE PARAMETER = 'NLS_CHARACTERSET'
-- JA16SJISTILDE

SJISのデータベースの文字列カラムにUnicodeの絵文字を格納するには、NVARCHAR2型のカラムを使います。次にJavaではシステムプロパティに-Doracle.jdbc.defaultNChar=trueを設定します。

http://otndnld.oracle.co.jp/document/products/oracle10g/102/doc_cd/java.102/B19275-03/global.htm

これだけでいいのですが、-Doracle.jdbc.defaultNChar=trueを設定しているとき、NVARCHAR2列とVARCHAR2列が混在するテーブルでwhere句にインデックスを付与したVARCHAR2列を指定すると、なんとインデックスが使われず、フルスキャンとなります。

SELECT * FROM hoge WHERE fuga = ?
-- fugaはVARCHAR2列でインデックスがある

実は、-Doracle.jdbc.defaultNChar=trueを設定するとデータベースによって自動的に内部関数が適用されるからです。

SELECT * FROM hoge WHERE fuga = SYS_OP_C2C(?)

SYS_OP_C2Cとは何の関数か、こちらに説明があります。

Oracle SQL execution plan changes due to SYS_OP_C2C internal conversion - Stack Overflow

SYS_OP_C2C is an internal function which does an implicit conversion of varchar2 to national character set using TO_NCHAR function

VARCHAR2列の値をTO_NCHAR関数を使って変換するものでした。回避策は他にもありそうですが、単純にこのSYS_OP_C2C関数を適用した関数インデックスを作ると、当然関数インデックスが使われてインデックスアクセスとなります。

でも一番はキャラクタセットを変換することでしょうね。Javaの話というよりOracleの話になっちゃいました…