none
nvarcharに日本語を入力すると?????????になる RRS feed

  • 質問

  • AzureのSQLサーバに日本語データをINSERTやUPDATEすると?????????になってしまいます。

    過去ログを見て、データ定義をvarcharでなくnvarchar(max)にしているのですが、それでも?????????になります。

    データテーブルに直接手動で入力すると、問題なく日本語が入力できます。プログラムで日本語を入力すると?????????になってしまいます。また同じ場所にアルファベットのデータを入力すると、正しく入力できます。

    何か設定などあるのでしょうか。

    2016年1月15日 8:42

回答

すべての返信

  • > プログラムで日本語を入力すると?????????になってしまいます。

    その「プログラム」というのは具体的にどうなっているのでしょう。コードを開示できないでしょうか?

    また「?????????」になるというのはどのように確認しているのですか?

    2016年1月15日 8:47
  • こんにちは。

    直接手動で、はどのようなツールでどのような手順で行ったのでしょうか
    まずは以下を確認してみては如何でしょうか。

    http://blogs.msdn.com/b/dsazurejp/archive/2012/06/28/unicode-nchar-nvarchar.aspx

    • 回答の候補に設定 星 睦美 2016年1月18日 5:33
    • 回答としてマーク 星 睦美 2016年1月19日 5:47
    2016年1月15日 8:50
    モデレータ
  • ご回答ありがとうございます。こちらにまとめてレスさせていただきます。

    ご教示いただいたページの情報のとおりで、SQLコマンド列にNを付加したら日本語のINSERTとREPLACEが正常に動作するようになりました。

    手動での動作の確認とは、Visual Studioの「サーバーエクスプローラー」→「SQL Server Object Explorer」でデータベースのテーブルを表示させて、表示されるセルの中の情報を手入力・手修正しました。

    このときSQLのテーブルvarcharで設計していたら手動でも?????????となるのでnvarcharに変更しました。すると手動なら日本語を入力できるようになったのですが、プログラムから日本語をINSERT、REPLACEしたら?????????になるので困っていた次第です。

    まだAzureの全体像をよく理解できておらず、ローカル環境で作成して動作確認したソフトウェアを修正する必要があることが判りました。

    2016年1月15日 21:47
  • > プログラムから日本語をINSERT、REPLACEしたら?????????になるので困っていた次第です。

    その「プログラム」というのは SQL の INSERT クエリだったようですね。

    先に ASP.NET Web アプリの質問をされていたので、Web アプリのプログラムのことと思っていました。


    > ローカル環境で作成して動作確認したソフトウェアを修正する必要があることが判りました。

    で、その「ソフトウェア」というのは何でしょうか? もし先の質問の Web アプリのことだとすると、MCV4 とのことですので、Visual Studio で EDM を作って使っていると思いますが、それでも文字化けしてしまいますか?


    【追伸】

    ちなみに、クエリをパラメータ化して ADO.NET + SqlClient 経由で INSERT, UPDATE をかければ、照合順序が Japanese_CI_AS(デフォルト)でも SQL_Latin1_General_CP1_CI_AS(Azure のデフォルトらしい)でも文字化けはしないです。(一応検証して確認しました)

    なので、「ソフトウェア」というのがそのように作ってあれば修正しなくても問題ないと思うのですが。

    もし、文字化けするようであれば、どのようにその「ソフトウェア」を作ったかの情報を提供していただけると幸いです。

    • 編集済み SurferOnWww 2016年1月16日 3:13 追伸追加
    2016年1月16日 2:07
  • たとえばINSERTでしたら下記のようにコーディングしています。

    using System.Data.SqlClient;
    ・・・・・中略・・・・・
    SqlConnection SQLconnection = new SqlConnection(SQL接続文字列・・・
    SqlCommand cmdSQLtableINSERT = new SqlCommand("INSERT INTO table (各レコードに挿入するデータ)", SQLconnection);
    SQLconnection.Open();
    cmdSQLtableINSERT.ExecuteNonQuery();
    SQLconnection.Close();

    try~catch省略しています。
    上記の「各レコードに挿入するデータ」でTak1wa氏ご教示のとおり日本語レコードの前にNプレフィックスを付加しました。
    これで日本語の?????????が解消されて、正しく日本語が設定できるようになりました。こちらではNプレフィックスなしでは必ず日本語は文字化けします。
    ずっとこの方法でSQLを操作しており、Azureではじめて日本語の問題に出くわしました。

    2016年1月16日 7:57
  • 私のブログの記事ですが、一応、ご紹介しておきます。

    SQL ServerのNプレフィックス
    http://d.hatena.ne.jp/trapemiya/20140422/1398128896


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

    • 編集済み trapemiya 2016年1月16日 8:17 ハイパーリンク設定
    • 回答の候補に設定 星 睦美 2016年1月18日 5:33
    • 回答としてマーク 星 睦美 2016年1月19日 5:46
    2016年1月16日 8:16
  • INSERT クエリをパラメータ化してないですよね? だから文字化けするのではないかと思いますが。今時間がないので後でどのようにするか書きます。

    ちなみに、パラメータ化クエリとはどういうものかというと、自分のブログで恐縮ですか、以下のようなことです。

    パラメータ化クエリ
    http://surferonwww.info/BlogEngine/post/2012/02/02/Parameterized-query.aspx

    パラメータ化は SQL インジェクション防止とパフォーマンスの向上に必須と言っていいです。必ず使うようにしましょう。

    • 回答の候補に設定 星 睦美 2016年1月18日 5:32
    • 回答としてマーク 星 睦美 2016年1月19日 5:46
    2016年1月16日 8:36
  • 先のレスで、

    > INSERT クエリをパラメータ化してないですよね? だから文字化けするのではないかと思いますが。
    > 今時間がないので後でどのようにするか書きます。

    と書きましたが、時間が取れたのでパラメータ化あり / なしでどう違うかの例を書きます。

    まず、サンプルとして SQL Server 2008 Express に照合順序が SQL_Latin1_General_CP1_CI_AS(Azure のデフォルトらしい)のデータベースを作りました。以下の通りです。

    それに以下のコードで INSERT してみます。上のクエリがパラメータ化なし、下のクエリがパラメータ化ありです。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Data;
    using System.Data.SqlClient;
    
    namespace ConsoleApplication7
    {
        class Program
        {
            static void Main(string[] args)
            {
                string connString = @"Data Source=.\SQLEXPRESS;Initial Catalog=SQLLatin1;Integrated Security=True";
    
                using(SqlConnection conn = new SqlConnection(connString))
                {
                    conn.Open();
                    string query = "INSERT INTO [Table] ([Name]) VALUES ('あいうえお')";
                    using(SqlCommand cmd = new SqlCommand(query, conn))
                    {
                        cmd.ExecuteNonQuery();
                    }
    
                    query ="INSERT INTO [Table] ([Name]) VALUES (@Name)";
                    using(SqlCommand cmd = new SqlCommand(query, conn))
                    {
                        cmd.Parameters.Add(new SqlParameter("@Name", SqlDbType.NVarChar, 50));
                        cmd.Parameters["@Name"].Value = "かきくけこ";
                        cmd.ExecuteNonQuery();
                    }
                } 
            }
        }
    }

    結果は以下の通りです。赤枠で囲ったものがパラメータ化なし、青枠で囲ったものがパラメータ化ありです。

    というわけで、クエリをパラメータ化して ADO.NET + SqlClient を使えば(普通のやり方)、照合順序が SQL_Latin1_General_CP1_CI_AS だからと言って文字化けに悩むことはなさそうです。



    • 編集済み SurferOnWww 2016年1月16日 12:27 誤記訂正
    2016年1月16日 11:03
  • ご教示のとおりパラメータ化を試してみました。確かにNプレフィックスなしで日本語の文字化けがなくなります。

    ブログ拝見いたしました。パラメータ化で悪いことはなさそうなので、パラメータ化を使用する方向でプログラム修正したいと思います。Nプレフィックスもつけようと思います。

    これまでWindows Server上でMySQLしか使ったことがありませんでした(価格の問題で会社の方針)。MySQLではこういう問題はなかったので、MS SQLの日本語入力には深い問題があることが判りました。ありがとうございます。


    2016年1月16日 21:34
  • > パラメータ化で悪いことはなさそうなので、

    もし、例えばユーザー入力をリテラルに使ってクエリを組み立てるというようなことをしていて、SQL インジェクションの可能性があるなら、パラメータ化は「「悪いことはなさそう」というようなレベルの話ではなくて、セキュリティ対策の一つとして絶対にやらなければいけないことだと思うのですが・・・

    私が言っているのは「パラメータ化というセキュリティ対策として普通にやるべきことをやっていれば、今回のような文字化けに悩むことはなさそう」ということです。(「パラメータ化が文字化け対策」と言っているのではありません)

    > MySQLではこういう問題はなかったので、

    MySQL でも Connector/Net を使ってパラメータ化クエリを使った .NET アプリが作れます。

    文字化け対策ということではなくてセキュリティ対策として MySQL でもパラメータ化クエリを使用することをお勧めします。

     

    #今回の話とは関係ないことですが、MySQL でも Character Set の設定を間違えると問題が出るはずです。

    #ついでに自分の失敗談を書いておくと、MySQL のインストールの際に Character Set の設定を Best Support For Multilingualism にしても(UTF-8 になる)、日本語を使うと Incorrect string value というエラーになって悩んだことがありました。

    #実は、米国製のテーブル生成スクリプトを走らせてテーブル作った際、スクリプトに DEFAULT CHARSET=latin1 が指定されている(latin1 が MySQL のデフォルトらしい)ことに気がつかなかったというオソマツだったのですが。

    2016年1月17日 4:03