1つのSQLテーブル/列から別のSQLテーブルに値を挿入しています。他の理由によりこれらの列のデータ型は異なりますが、SQL Server 2014でnvarchar(10)のソースとchar(10)の宛先が原因でエラーが発生することがある理由がわかりません。
文字列型やバイナリは省略されます。
len(sourcecol)= 10およびdatalength(sourcecol)= 20。
Nvarcharタイプのソース列に格納されているいくつかの非表示のスペース/文字が原因である可能性がありますか?
OPからのデータとテーブルDDLの例がないと、このエラーの正確な原因がOPのであるかどうかを特定することは困難ですただし、これは以下の理由により、他の人に動作(したがって問題)が発生する可能性があります。
一部のコードページ(各CHAR
/VARCHAR
フィールドの照合順序によって決定される)では、1バイトに収まる256文字を超えるマップを可能にするために、2バイト文字を使用できます。文字列フィールドの最大長は実際には文字ではなくバイトの問題であるため、最大長が10のCHAR
/VARCHAR
フィールドは10バイトしか保持できず、どこからでも保持できます。 5-10文字。ソースフィールドがNVARCHAR(10)
の場合、これらの10文字は、VARCHAR
フィールドで1バイトを超える文字にマップできます。このエラーが発生するのに必要なのは、9つの「通常の」シングルバイト文字と11の実際のバイトを必要とする1つのダブルバイト文字だけです。
したがって、宛先テーブルのCHAR(10)
フィールドの照合順序を確認してください。そのフィールドの照合順序の名前がわかったら、それがどのコードページが使用されているかを示し、932、936、949、または950のいずれかである場合、これが問題の原因である可能性が非常に高くなります。この場合、宛先フィールドを変更して最大長を20にします(10文字すべてが2バイトの場合に安全を確保するため)。私はVARCHAR(20)
をお勧めしますが、本当に空白埋めが本当に好きなら、CHAR(20)
を実行してください。
少しより詳細な説明が続きます:
VARCHAR
(つまり8ビット拡張ASCII)データはシングルバイトであり、NVARCHAR
(つまりUnicode)データはダブルバイトであることが一般的に理解されています。文字列データ(およびエンコーディング)のこの理解は、米国英語のアルファベット(および他のかなりの数の、少なくともほとんどの「アクティブ」な言語)を扱うときに常に当てはまります。そして、ほとんどの言語で作業しているときはそうですが、それは常に正しいとは限りません。
この理解がUnicodeデータに当てはまらない方法は、この質問の範囲を超えているため、ここでは詳しく説明しません。
しかし、私たちの旧友であるVARCHAR
に関しては、文字が1バイトではなく2バイトを占めることを許容する状況がいくつかあります。はい、あなたはそれを正しく読みました。しかし、どうやって?まあ、Unicodeが登場する前は、255文字を超えるアルファベットを含む一部の文化では、ネイティブアルファベットを使用したいと考えていました。シングルバイト(256の値の範囲)内ではそれができないため、2バイト文字セット(DBCS)が考案されました。これらは異なるコードページとして処理され、WindowsとSQL Serverはそのうちの4つをサポートします。
「2バイト」という用語がNVARCHAR
のようなものであり、16ビットシーケンスでのみ機能するという意味と混同しないでください。これらのコードページは、実際にはUTF-8に似た可変長エンコーディングであり、少なくとも最初の128の値(0〜127)と(128〜255の範囲)の一部に1バイト(8ビット)を使用します。
これは切り捨てエラーにどのように適合しますか?まあ、NVARCHAR
とVARCHAR
の両方のデータ型の最大長は、文字ではなくバイトで表現されています。つまり、VARCHAR(10)
は、10バイト未満の文字が10バイトに収まる場合でも、最大10バイトです。同様に、NVARCHAR(10)
は、20バイトに収まる文字が10文字未満であっても、最大20バイトです。
これを念頭に置いて、Unicodeからコードページに変換すると、同じ文字へのマッピングが試行されることがわかります。ほとんどのコードページでは、これらの文字のサイズはすべて1バイトです。しかし、4つのDBCSコードページには、存在する(したがってマップできる)かなりの数の文字があり、2バイトです(そうでない場合は存在しません)。
ここでの問題は、DBCSコードページが(宛先に対して)使用されており、マップされている文字の少なくとも1つがVARCHAR
タイプ内で2バイトを使用していることです。
以下は、この動作の実際の例です。
テスト設定
まず、このコードを実行してテストを設定します。このテストを実行するデータベースのデフォルトの照合順序は重要ではありません。ここでは、メインのUnicode文字セットの最初の65,536個のコードポイントのそれぞれを保持する一時テーブルを作成しています(最初の65,536を超える文字には2つのコードポイントが必要で、それぞれ4バイトですが、ここでも範囲外です。現在の問題に関連する動作を変更します;)
SET NOCOUNT ON;
IF (OBJECT_ID(N'tempdb..#Source') IS NOT NULL)
BEGIN
DROP TABLE #Source;
END;
CREATE TABLE #Source ([Character] NVARCHAR(1));
;WITH nums (num) AS
(
SELECT TOP (65536) ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM [master].[sys].[columns] sc1
CROSS JOIN [master].[sys].[columns] sc2
)
INSERT INTO #Source ([Character])
SELECT NCHAR(num - 1)
FROM nums;
SELECT * FROM #Source;
テスト1:Latin1文字セット(コードページ1252)
以下を実行してもエラーは発生しません。うまくいきます。ただし、最初の256個の値のみが変換されます。これは、任意の1バイト文字セット(SBCS)に適合するすべての値であるためです。
IF (OBJECT_ID(N'tempdb..#Destination_CP1252') IS NOT NULL)
BEGIN
DROP TABLE #Destination_CP1252;
END;
CREATE TABLE #Destination_CP1252 ([Character] VARCHAR(1) COLLATE Latin1_General_100_CI_AS);
INSERT INTO #Destination_CP1252 ([Character])
SELECT [Character]
FROM #Source;
SELECT [Character],
LEN([Character]) AS [NumberOfCharacters],
DATALENGTH([Character]) AS [NumberOfBytes],
CONVERT(VARBINARY(2), [Character]) AS [BinaryValue]
FROM #Destination_CP1252;
テスト2:日本語(Shift-JIS)文字セット(コードページ932)
IF (OBJECT_ID(N'tempdb..#Destination_CP932') IS NOT NULL)
BEGIN
DROP TABLE #Destination_CP932;
END;
CREATE TABLE #Destination_CP932 ([Character] VARCHAR(1) COLLATE Japanese_Unicode_CI_AS);
INSERT INTO #Destination_CP932 ([Character])
SELECT [Character]
FROM #Source;
上記のコードを実行すると、次のエラーが発生します。
メッセージ8152、レベル16、状態2、行1
文字列型やバイナリは省略されます。
ステートメントは終了されました。
これが2バイト文字セット(DBCS)変換の問題である場合は、フィールドサイズを1バイト増やすと修正されます。最初に、テーブルに実際に何も挿入されていないことを確認します(次のINSERT
ステートメントがデータを入れるものであることを確実にするため)。
SELECT *
FROM #Destination_CP932;
-- no rows
すごい。次を実行します。
ALTER TABLE #Destination_CP932
ALTER COLUMN [Character] VARCHAR(2) COLLATE Japanese_Unicode_CI_AS;
INSERT INTO #Destination_CP932 ([Character])
SELECT [Character]
FROM #Source;
エラーはありません。ウフー!変換された文字を見てみましょう:
SELECT [Character],
LEN([Character]) AS [NumberOfCharacters],
DATALENGTH([Character]) AS [NumberOfBytes],
CONVERT(VARBINARY(2), [Character]) AS [BinaryValue]
FROM #Destination_CP932
WHERE 1 = 1
--AND DATALENGTH([Character]) > 1
--AND [Character] <> '?'
結果をスクロールする場合は、[NumberOfBytes]
および[BinaryValue]
フィールドに注意する必要があります。
2バイト値だけを表示するには、次の行のコメントを外して、再実行します。
AND DATALENGTH([Character]) > 1
コードページ932の値の全範囲を表示するには、DATALENGTH
where条件を再度コメント化し、次の行のコメントを外して再実行します。
AND [Character] <> '?'
カウントに9483文字が表示されます。公平を期すために、除外された実際の?
文字を考慮して1を追加すると、VARCHAR
フィールド????で表される合計9484文字の付与が得られます。