none
「柹」という特殊文字をoracleに登録できません。 RRS feed

  • 質問

  • 「柹」という文字をoracleに登録しようとしています。

    Oracleに問合せしたところ下記の対処にてsqldeveloperからは登録できるようになりました。

    1.データベースの項目定義をNVARCHAR2にする。

    2.SQLを UPDATE STAFFTBL SET name = N'柹’ where ・・のようにN'柹’と表記する。

    3.sqldeveloperの「ツール」「プリファレンス」から環境の「エンコーディング」を"UTF-16LE"にする。

    C#プログラム(Windowsアプリ)から同様にSQLを発行していますが、ORACLEに登録された文字

    は「?」となり文字化けしています。

    上記3のsqldeveloperにてUTF-16LEに設定した部分をどのようにc#でコーディングしたらいいか

    わからず困っています。

    初心者なので質問があいまいかもしれませんが、宜しくお願いいたします。

    • 移動 星 睦美 2016年10月18日 1:50 Windows クライアント開発 から
    2016年10月18日 1:31

すべての返信

  • フォーラム オペレーターの星 睦美です。coffeetime0831 さん、投稿ありがとうございます。

    私のほうで今回の質問をVisual C#に関する話題を扱うフォーラムに移動しました。フォーラムのユーザーから参考になる回答がありましたら、投稿者からの[回答としてマーク] をお願いします。


    フォーラム オペレーター 星 睦美 - MSDN Community Support

    2016年10月18日 1:57
  • こんにちは。

    SqlDeveloperにて正常に登録できた特殊文字を、
    クライアントのSQL*PlusでSelectした場合はどのように表示されますか。

    2016年10月18日 2:18
    モデレータ
  • 加えてC#側の具体的なコードも挙げていただきたいです。
    2016年10月18日 2:46
  • 接続に使用されているプロバイダーはODP.NETでしょうか?
    ちなみに以下からインストールできます。

    Oracle Developer Tools for Visual Studio
    http://www.oracle.com/technetwork/jp/developer-tools/visual-studio/overview/index.html


    ★良い回答には回答済みマークを付けよう! MVP - .NET  http://d.hatena.ne.jp/trapemiya/

    2016年10月18日 5:24
    モデレータ
  • こんにちは 返信有難うございます。

    SqlDeveloperでSELECTした結果はただしく「柹」となっていますが、

    SQL*PlusでSELECTした結果は「?」となっております。

    また、クライアント端末(Windows7)のC#プログラムでSELECTした結果は「柹」となっています。



    2016年10月18日 5:54
  • 試してないので想像なのですが、おそらくクライアントPCにNLS_LANGを登録していると思います。
    それをUTF-16LEに対応したものに変更すれば、SQL*Plusでもご自身のアプリでも正常に表示できるでしょうか。

    またはアプリ内でOracleConnectionインスタンスを生成した時に、
    セッション・グローバリゼーション設定でキャラクタセットを変更してみてはどうでしょう。

    http://otndnld.oracle.co.jp/document/products/oracle10g/101/doc_v12/win.101/B15519-01/featGlobal.htm#i1008314
    https://docs.oracle.com/cd/E16338_01/win.112/b66456/OracleGlobalizationClass.htm#i1009565

    2016年10月18日 6:21
    モデレータ
  • 加えてC#側の具体的なコードも挙げていただきたいです。

    こんにちは、返信有難うございます。

    オラクルとの接続部分は下記のとおりです。

        class JSVDBAccess
        {
            public DataSet ReferTable(string strSQL)
            {

                OleDbConnection conn = new OleDbConnection();
                OleDbCommand command = new OleDbCommand();
                OleDbDataAdapter adapter = new OleDbDataAdapter();
                DataSet ds = new DataSet();

                conn.ConnectionString = @"Provider=OraOLEDB.Oracle;User ID=js;Password=xxxxxx;Data Source=XXX;";

                command.Connection = conn;
                command.CommandText = strSQL;

                adapter.SelectCommand = command;

                adapter.Fill(ds);
                return ds;
            }

            public bool ModifyTable(string strSQL)
            {

                OleDbConnection conn = new OleDbConnection();
                OleDbCommand command = new OleDbCommand();

                try
                {
                    conn.ConnectionString = @"Provider=OraOLEDB.Oracle;User ID=js;Password=xxxxx;Data Source=XXXXX;";

                    command.Connection = conn;
                    command.CommandText = strSQL;
                    
                    conn.Open();
                    command.ExecuteNonQuery();

                    return true;
                }
                catch (Exception)
                {
                    return false;
                }
                finally
                {
                    conn.Close();
                }
            }
        }


    2016年10月18日 6:34
  • それほど私も詳しいわけではありませんが、OleDbConnectionやOleDbCommandではなく、OracleConnectionとかOracleCommandクラスを使う方が良いように思います。間違っていたらごめんなさい。
    ただ、実際にそうやってコードを書いた経験があります。

    ★良い回答には回答済みマークを付けよう! MVP - .NET  http://d.hatena.ne.jp/trapemiya/

    2016年10月18日 7:44
    モデレータ
  • trapemiyaさんも指摘されていますが.NETからOracleへの接続は

    といくつかの方法があります。

    挙げられたコードを見る限りOLE DBを使用されています。Oracle Provider for OLE DBのドキュメントを読む限りUnicodeモードと非Unicodeモードが存在するようで、行セットおよびパラメータのデータ型マッピングによると非Unicodeモードでは一旦Shift-JISに変換されるようです。しかしクライアント側から明示的にUnicodeモードで接続する方法はドキュメントからは見つけられませんでした。関係あるかわかりませんがOLEDB.NETデータ・プロバイダの互換性によると接続文字列に「OLEDB.NET=True」を追加すべきです。

    なお、このような問題を回避するためにも余計な中間層を挟まないODP.NETを選択した方がいいかもしれません。

    2016年10月18日 8:08
  • 返信がおそくなりました。

    ODP.NETに修正して実施しましたが、問題は解決しませんでした。

    以下、修正したデータベースに接続するクラスです。

        class JSVDBAccess
        {
            public DataSet ReferTable(string strSQL)
            {

                OracleConnection conn = new OracleConnection();
                OracleCommand command = new OracleCommand();
                OracleDataAdapter adapter = new OracleDataAdapter();
                DataSet ds = new DataSet();

                conn.ConnectionString = @"User ID=js;Password=*****;Data Source=JSVPDB;";
                command.Connection = conn;
                command.CommandText = strSQL;

                adapter.SelectCommand = command;

                adapter.Fill(ds);
                return ds;
            }

            public bool ModifyTable(string strSQL)
            {

                OracleConnection conn = new OracleConnection();
                OracleCommand command = new OracleCommand();

                try
                {
                    conn.ConnectionString = @"User ID=js;Password=*****;Data Source=JSVPDB;";

                    command.Connection = conn;
                    command.CommandText = strSQL;
                    
                    conn.Open();
                    command.ExecuteNonQuery();

                    return true;
                }
                catch (Exception)
                {
                    return false;
                }
                finally
                {
                    conn.Close();
                }
            }
        }
    2016年10月24日 1:31
  • クエリ(ModifyTable メソッドの strSQL)はどうなっているのですか?

    見たところパラメータ化されてないですよね。SQL Server の場合ですが、以前以下のような話がありました。

    nvarcharに日本語を入力すると?????????になる
    https://social.msdn.microsoft.com/Forums/ja-JP/4de12921-4769-423e-9b39-ba36599fb3f0/nvarchar?forum=sqlazureja

    パラメータ化すると問題はなくなるかもしれません。試してみてはいかがでしょう? (そもそも、パラメータ化クエリを使用するということは、セキュリティ対策として普通にやるべきことなのですが)

    パラメータ化の副次的な効用
    http://surferonwww.info/BlogEngine/post/2016/06/04/use-parameterized-query-to-avoid-unexpected-character-corruption.aspx


    #今回の問題とは関係ない余計はお世話かもしれませんが、Exception をキャッチするのは止めた方がいいです。理由は以下の記事を見てください。

    NETの例外処理 Part.1
    https://blogs.msdn.microsoft.com/nakama/2008/12/29/net-part-1/

    .NETの例外処理 Part.2
    https://blogs.msdn.microsoft.com/nakama/2009/01/02/net-part-2/

    #.NET 4 からは破損状態例外は catch できなくなっているそうですが、「それでも Catch (Exception e) を使用するのはよくない」ということについては以下の記事を見てください。

    破損状態例外を処理する
    https://msdn.microsoft.com/ja-jp/magazine/dd419661.aspx



    • 編集済み SurferOnWww 2016年10月24日 1:55 リンク貼り直し
    2016年10月24日 1:52
  • 返信おそくなりました。

    OracleConnectionインスタンスを生成した時に、セッション・グローバリゼーション設定でキャラクタセットを変更しよう

    としましたが、該当するプロパティーがないようです。ClientCharacterSetかとおもいましたが、読み取り専用プロパティー

    のようです。


    2016年10月24日 2:04
  • クエリ(ModifyTable メソッドの strSQL)はどうなっているのですか?

    クエリは以下のとおりです。

    UPDATE
        STAFFTR
    SET
        staffcd = '***',
        name = N'柹* **',
        phonetic = 'かき* **',
        departmentcd = '900010',
     ・・・

    WHERE
        staffcd = '***'

    2016年10月24日 2:13
  • 「問題は解決しません」だけの記述では他者からみて現在の状況がまったくわかりません。(例えばフォントだけの問題かもしれません。)

    とりあえずODP.NETへの移行ガイド

    Unicode -- This setting is unnecessary because ODP.NET is always Unicode aware

    とありました。ODP.NETを使用していてUnicodeが正しく表示されないのだとしたら、ODP.NETもしくはデータベース、いずれにしてもOracle側の問題かと思います。

    • 回答の候補に設定 星 睦美 2016年10月27日 1:30
    2016年10月24日 2:31
  • > クエリは以下のとおりです。

    'か' や 'き' は N プレフィックス無しでも文字化けしなくて、'柹' は N プレフィックスをつけても文字化けする(? になる)ということなのでしょうか?

    '柹' は UTF-16 で 2 バイトで表せる Unicode の基本多言語面に属する文字で、その意味では 'か' や 'き' と同じです。違いは Shift_JIS に含まれるか否か('柹' は Shift_JIS には含まれない)ぐらいだと思うのですが・・・

    パラメータ化は関係なさそうですね。Oracle 製品のことは Oracle のコミュニティで聞いたほうがいいかもしれません。

    • 回答の候補に設定 星 睦美 2016年10月27日 1:30
    2016年10月24日 3:02
  • 実験してみましたが、文字列リテラル指定では、期待通りに渡すことができませんでした。
    状況から判断すると、「柹」の文字が「'」に置換されてから渡されてしまっているように見えます。
    (OS / Oracle / Oracle Client / ODP.NET Managed Driver のバージョンにもよるのかも?)

    とりあえずパラメーターで渡す分には成功したように見えます。
    あるいは文字列埋め込みにするなら、RAW データ経由で渡すことはできるようで。

    using Oracle.ManagedDataAccess.Client;
    using System;
    using System.Diagnostics;
    using System.IO;
    
    namespace ConsoleApplication1
    {
        public static class Program
        {
            static void Main()
            {
                File.Delete(Properties.Settings.Default.LogFile);
    
                using (var con = new OracleConnection())
                using (var cmd = new OracleCommand())
                {
                    con.ConnectionString = Properties.Settings.Default.ConnectionString;
                    con.Open();
                    cmd.Connection = con;
                    cmd.CommandText = @"
    DECLARE
      CURSOR CSR IS SELECT TABLE_NAME FROM USER_TABLES WHERE TABLE_NAME = 'STAFFTBL';
    BEGIN
      FOR REC IN CSR LOOP
        EXECUTE IMMEDIATE 'DROP TABLE ' || REC.TABLE_NAME || ' PURGE';
      END LOOP;
    
      EXECUTE IMMEDIATE '
        CREATE TABLE STAFFTBL
        ( PKEY NUMBER(5) PRIMARY KEY
        , NAME NVARCHAR2(40)
        )
      ';
    END;
    ";
                    // 下記を実行すると、STAFFTBL が再作成されるので注意
                    // cmd.ExecuteNonQuery();
    
                    // [OracleException] ORA-01756: 引用符付き文字列が正しく終了していません
                    // cmd.CommandText = "INSERT INTO STAFFTBL VALUES ( 1220, N'\u67F9' )";
                    // cmd.ExecuteNonQuery();
    
                    // 登録はされるが、格納されるデータは「'」になる
                    // DUMP(NAME, 1016) は 「Typ=1 Len=2 CharacterSet=AL16UTF16: 0,27」
                    cmd.CommandText = "INSERT INTO STAFFTBL VALUES ( 1222, N'\u67F9'' )";
                    cmd.ExecuteNonQuery();
    
                    // 登録はされるが、格納されるデータは「'」になる
                    // DUMP(NAME, 1016) は 「Typ=1 Len=2 CharacterSet=AL16UTF16: 0,27」
                    cmd.CommandText = "INSERT INTO STAFFTBL VALUES ( 1224, N''\u67F9' )";
                    cmd.ExecuteNonQuery();
    
                    // [OracleException] ORA-01756: 引用符付き文字列が正しく終了していません
                    // cmd.CommandText = "INSERT INTO STAFFTBL VALUES ( 1226, N''\u67F9'' )";
                    // cmd.ExecuteNonQuery();
    
                    // [OracleException] ORA-01756: 引用符付き文字列が正しく終了していません
                    // cmd.CommandText = "INSERT INTO STAFFTBL VALUES ( 1240, '\u67F9' )";
                    // cmd.ExecuteNonQuery();
    
                    // 登録はされるが、格納されるデータは「'」になる
                    // DUMP(NAME, 1016) は 「Typ=1 Len=2 CharacterSet=AL16UTF16: 0,27」
                    cmd.CommandText = "INSERT INTO STAFFTBL VALUES ( 1242, '\u67F9'' )";
                    cmd.ExecuteNonQuery();
    
                    // 登録はされるが、格納されるデータは「'」になる
                    // DUMP(NAME, 1016) は 「Typ=1 Len=2 CharacterSet=AL16UTF16: 0,27」
                    cmd.CommandText = "INSERT INTO STAFFTBL VALUES ( 1244, ''\u67F9' )";
                    cmd.ExecuteNonQuery();
    
                    // [OracleException] ORA-01756: 引用符付き文字列が正しく終了していません
                    // cmd.CommandText = "INSERT INTO STAFFTBL VALUES ( 1246, ''\u67F9'' )";
                    // cmd.ExecuteNonQuery();
    
    
    
                    // [OracleException] ORA-01756: 引用符付き文字列が正しく終了していません
                    // cmd.CommandText = "INSERT INTO STAFFTBL VALUES ( 1420, N'柹')";
                    // cmd.ExecuteNonQuery();
    
                    // 登録はされるが、格納されるデータは「'」になる
                    // DUMP(NAME, 1016) は 「Typ=1 Len=2 CharacterSet=AL16UTF16: 0,27」
                    cmd.CommandText = "INSERT INTO STAFFTBL VALUES ( 1422, N'柹'')";
                    cmd.ExecuteNonQuery();
    
                    // 登録はされるが、格納されるデータは「'」になる
                    // DUMP(NAME, 1016) は 「Typ=1 Len=2 CharacterSet=AL16UTF16: 0,27」
                    cmd.CommandText = "INSERT INTO STAFFTBL VALUES ( 1424, N''柹')";
                    cmd.ExecuteNonQuery();
    
                    // [OracleException] ORA-01756: 引用符付き文字列が正しく終了していません
                    // cmd.CommandText = "INSERT INTO STAFFTBL VALUES ( 1426, N''柹'')";
                    // cmd.ExecuteNonQuery();
    
                    // [OracleException] ORA-01756: 引用符付き文字列が正しく終了していません
                    // cmd.CommandText = "INSERT INTO STAFFTBL VALUES ( 1440, '柹')";
                    // cmd.ExecuteNonQuery();
    
                    // 登録はされるが、格納されるデータは「'」になる
                    // DUMP(NAME, 1016) は 「Typ=1 Len=2 CharacterSet=AL16UTF16: 0,27」
                    cmd.CommandText = "INSERT INTO STAFFTBL VALUES ( 1442, '柹'')";
                    cmd.ExecuteNonQuery();
    
                    // 登録はされるが、格納されるデータは「'」になる
                    // DUMP(NAME, 1016) は 「Typ=1 Len=2 CharacterSet=AL16UTF16: 0,27」
                    cmd.CommandText = "INSERT INTO STAFFTBL VALUES ( 1444, ''柹')";
                    cmd.ExecuteNonQuery();
    
                    // [OracleException] ORA-01756: 引用符付き文字列が正しく終了していません
                    // cmd.CommandText = "INSERT INTO STAFFTBL VALUES ( 1446, ''柹'')";
                    // cmd.ExecuteNonQuery();
    
    
                    // 登録はされるが、格納されるデータは「'」になる
                    // DUMP(NAME, 1016) は 「Typ=1 Len=2 CharacterSet=AL16UTF16: 0,27」
                    cmd.CommandText = "INSERT INTO STAFFTBL VALUES ( 1600, N'柹柹')";
                    cmd.ExecuteNonQuery();
    
    
                    // 正常終了
                    // DUMP(NAME, 1016) は 「Typ=1 Len=2 CharacterSet=AL16UTF16: 67,f9」
                    cmd.CommandText = "INSERT INTO STAFFTBL VALUES "
                        + " ( 2200, UTL_RAW.CAST_TO_NVARCHAR2(HEXTORAW('67F9')) )";
                    cmd.ExecuteNonQuery();
    
                    // 正常終了
                    // DUMP(NAME, 1016) は 「Typ=1 Len=2 CharacterSet=AL16UTF16: 67,f9」
                    cmd.CommandText = "INSERT INTO STAFFTBL VALUES ( :p1, :p2 )";
                    cmd.Parameters.Clear();
                    cmd.Parameters.Add("p1", OracleDbType.Decimal).Value = 3200;
                    cmd.Parameters.Add("p2", OracleDbType.NVarchar2, 40).Value = "\u67F9";
                    cmd.ExecuteNonQuery();
                    cmd.Parameters.Clear();
    
                    // 正常終了
                    // DUMP(NAME, 1016) は 「Typ=1 Len=2 CharacterSet=AL16UTF16: 67,f9」
                    cmd.CommandText = "INSERT INTO STAFFTBL VALUES ( :p1, :p2 )";
                    cmd.Parameters.Clear();
                    cmd.Parameters.Add("p1", OracleDbType.Decimal).Value = 3400;
                    cmd.Parameters.Add("p2", OracleDbType.NVarchar2, 40).Value = "柹";
                    cmd.ExecuteNonQuery();
    
                    cmd.CommandText = "SELECT PKEY, NAME FROM STAFFTBL ORDER BY PKEY";
                    using (var reader = cmd.ExecuteReader())
                    {
                        while (reader.Read())
                        {
                            File.AppendAllText(Properties.Settings.Default.LogFile,
                                reader.GetInt32(0).ToString("D5") +
                                "\t" + reader.GetString(1) + "\r\n");
                        }
                    }
                    con.Close();
                }
                Process.Start("notepad.exe", Properties.Settings.Default.LogFile);
            }
        }
    
    }

    2016年10月25日 10:10