次の方法で共有


NVARCHAR/NCHAR データ型の列に格納されるデータが?になる

 

神谷 雅紀
Escalation Engineer

NVARCHAR や NCHAR などの Unicode 文字データ型に格納したデータが ? になるという事象についての問合わせが複数ありましたので、今回は、その事象について解説します。この事象は、特に、英語版 SQL Server に日本語データを格納するシステムや SQL Azure を使ったシステムで遭遇することが多いようです。

 

データが ? になる理由

データが ? になるのは、そのデータを非 Unicode から Unicode への変換ができなかったからです。

具体的な例を見てみます。データベースの照合順序が Latin1_General_CI_AS のデータベースに、特に照合順序を指定していない NVARCHAR 型列を持つテーブルを作成したとします。列の照合順序が指定されていない場合、データベースの照合順序が引き継がれますので、この列の照合順序は Latin1_General_CI_AS です。

use master
go
create database latin1gen collate latin1_general_ci_as
go
use latin1gen
go
create table dbo.t1 (c1 nvarchar(10))
go
select name,collation_name from sys.columns c where c.object_id=object_id('dbo.t1')
go

collation1

この列に対して、’あ’ を格納しようとします。

insert into t1 (c1) values ('あ')
go

しかし、テーブルには、「あ」は入らず「?」が入ります。

select c1 from t1
go

collation2

では、なぜ、この例では、「あ」は「?」になってしまうのでしょうか?

Japanese_CI_AS などの日本語照合順序での文字「あ」は 0x82A0 として表現されますが、上の例では、データベースの照合順序が Latin1_General であるため、「あ」を表現しているつもりの 0x82A0 は、実際には「あ」ではなく latin1_General の文字として扱われます。Latin1_General の文字 0x82A0 に対応する Unicode 文字は存在しません。その結果「あ」の変換結果は「?」となり、テーブルの列には「?」が格納されます。

国際化対応した Windows プログラムを書く人にはピンと来るものがあるのではないかと思います。MultiByteToWideChar Windows API も、指定したコードページに対応する文字がなければ、これと同じ挙動をします。

 

? にならないようにするための方法

非 Unicode/Unicode の変換が発生しないようにすることで、データが ? にならないようにすることができます。

そのためには、先の例の insert は、以下のように書き換える必要があります。

insert into t1 (c1) values (N'あ')
go

N プレフィックスを指定することで、この「あ」は Unicode 文字 0x3042 になり、insert 時に非 Unicode と Unicode の変換が発生しないため、データベースの照合順序が何であるのかに関わらず、日本語文字の「あ」としてテーブルの列に格納されます。

参考 : 定数 (Transact-SQL) - Unicode 文字列

もし、挿入する値をパラメータとして渡している場合には、T-SQL であれば、パラメータのデータ型を VARCHAR ではなく NVARCHAR として定義します。

sp_executesql N'insert into t1 (c1) values (@p1)', N'@p1 NVARCHAR(10)', N'あ'

 

プログラミングインタフェースごとのデータ型の指定方法は、使用しているインタフェースのマニュアルを確認して下さい。

SqlParameter クラス

SQLBindParameter Function

 

関連 : DO's&DONT's #2: 絶対にやらなければいけないこと - データ型を一致させる