web-dev-qa-db-ja.com

Oracle varchar2の文字列の16進値を表示しますか?

いくつかの異なる方法でエンコードされているが、テーブルの単一の列に保持されているテキストに問題があります。長い話。 MySQLでは、「select from hex(str)from table where」を実行でき、設定したとおりに文字列のバイトが表示されます。

Oracleでは、トルコ語の文字İで始まる文字列があります。これは、ユニコード文字0x0130の「ローマ字大文字とドット付き」です。これは、Unicodeバージョン2.0の本の印刷版です。 UTF-8では、この文字は0xc4b0です。

サポートが必要な非常に古いクライアントアプリがあります。彼らはこのテキストを "windows-1254"で送信します。以前は目を閉じて保管し、後で手渡していました。今、私たちはユニコードを必要とするか、ユニコードが与えられています。

ので、私は持っています:

SQL> select id, name from table where that thing;

ID     NAME
------ ------------------------
746    Ý

これは、「İ」がwindows-1254で0xddであり、wondows-1252で0xddが「Ý」であるためです。私の端末はおそらく通常のwindows-1252に設定されています。

だが:

SQL> select id, rawtohex(name) from table where that thing;

ID     RAWTOHEX(NAME)
------ ------------------------
746    C39D

MySQLにはhex(name)関数に相当するものはないようです。しかし、私はmust何かが欠けている。ここで何が欠けていますか?

私のJavaコードは、提供されたutf8を取得し、utf8コピーとwindows-1252コピーを保存する必要があります。Javaコードは、

bytes (utf8):  c4 b0
bytes (1254):  dd

それでも、保存すると、クライアントは正しい文字を取得できません。 Oracleが実際に保存したものを確認しようとすると、上記のゴミが表示されます。私はno C39Dがどこから来たのかを知っています。助言がありますか?

Ojdbc14.jarをすべてのアプリケーションに組み込み、「Oracle Database 11g Enterprise Editionリリース11.2.0.2.0-64bit Production」であると言うデータベースに接続しています。

13
Ray Kiddy

dump 関数を使用して、Oracleがデータを内部に保存する方法を確認します。

OracleがVARCHAR2文字セット変換をどのように扱うかについて誤解しているようです。Oracleがそのデータを物理的に格納する方法に影響を与えることはできません。 (まだ読んでいない場合は、以下を読むと役立ちます: 絶対に最低限必要なすべてのソフトウェア開発者は、ユニコードと文字セットについて絶対に、積極的に知っておく必要があります )。

クライアントはバイナリでのみOracleと対話します。実際、すべてのシステムはバイナリでのみ情報を交換します。お互いを理解するには、両方のシステムが使用されている言語(文字セット)を知っている必要があります。

あなたの場合、何が起こるかを再構築できます:

  1. クライアントはバイトddをOracleに送信し、windows-1252ではなく1254)と言います。
  2. Oracleは文字セットテーブルを検索し、このデータがこの文字セットのシンボルÝに変換されることを確認します。
  3. Oraclelogicallyは、この情報をテーブルに保存します。
  4. OracleはUTF-8に設定されているため、このデータをUTF-8Ýバイナリ表現に変換します。

    SQL> SELECT rawtohex('Ý') FROM dual;
    
    RAWTOHEX('Ý')
    --------------
    C39D
    
  5. OracleはC39Dを内部的に保存します。

ご覧のとおり、問題は最初のステップから来ています。セットアップの問題があります。これを修正しない限り、システムは正常に対話できません。

VARCHAR2を使用すると、変換はautomaticになります。これは、このデータ型が論理テキストシンボルインターフェイスであるためです。バイナリデータが保存されます)。

19
Vincent Malgrat

UTF-8で開始するバイトがあります。

String strFromUTF8 = new String(bytes, "UTF8");
byte[] strInOldStyle = strFromUTF8.getBytes("Cp1254");

MySQLで、これで完了です。これらのバイトを16進文字列に変換し、unhex(hexStr)で更新します。これにより、レガシーバイトをvarchar列に入れることができます。

Oracleでは、次のことを行う必要があります。

String again = new String(strInOldStyle, "Cp1254");
byte[] nextOldBytes = again.getBytes("UTF8");

これで、更新を実行して、バイトをvarchar2列に取得できます。

update table set colName = UTL_RAW.CAST_TO_VARCHAR2(HEXTORAW('hexStr')) where ...

奇妙な、違う?これが必要以上に複雑になったと確信しています。

しかし、私たちが見ているのはこれです

"İ" in UTF-8 == 0xc4d0
"İ" in Cp1254 == 0xdd == "Ý" in Cp1252
"Ý" in UTF-8 == 0xc3d9

したがって、文字列「İ」を取得して実行すると、次のようになります。

update table set name = UTL_RAW.CAST_TO_VARCHAR2(HEXTORAW('C3D9')) where ...

その後、レガシークライアントは「İ」を提供します。うん。できます。

5
Ray Kiddy