何らかの理由で、MS SQL Server 2016一括挿入がUnicode文字を誤って解釈/変換します。
つまり、Notepad ++とxxdはフラットファイルに0xC9があることを示していますが、一括挿入後にテーブルに "+"が表示され、SQL Serverでvarbinaryとしてキャストすると0x2Bと表示されます。バックアップにも0xC9があります。
25のフラットファイルをMS SQL Server 2016に一括挿入しています。これは15Gbのデータで、パイプ()フィールド区切り文字と[〜#〜] crlf [〜#を使用しています〜]行区切り文字。
提供されたバックアップの切り捨てられた構造に一括挿入します。バックアップと比較すると、違いがあります。注:データソースからのバックアップを25時間待つ必要がありますが、フラットファイルは15分で取得できます。
いくつかの違いは許容できますが(フラットファイルに適用している検索および置換)、多くはUnicode文字が誤って解釈されていることが原因です。
テーブルの例の構造は次のとおりです。
CREATE TABLE [dbo].[obfuscated_name](
[ob_1] [int] NOT NULL,
[ob_2] [int] NOT NULL,
[ob_3] [int] NOT NULL,
[ob_4] [nvarchar](300) NULL
) ON [PRIMARY]
データベース照合はデフォルトですSQL_Latin1_General_CP1_CI_AS。照合順序が異なる列はありません。この照合ではコードページ1252を使用する必要があります。これにより、問題のある文字が適切に解釈されます。
私のプロセスは常に流動的な本番データに対して実行されているため、他の変更がポップアップするのではないかと心配しています。問題を分離して手動で誤解を更新するのではなく、問題の原因を知りたいのです。
これはSQL Server(またはWindows)のバグではなく、ファイルを別のエンコーディング(つまり、「Unicode」)に変換する追加の手順が必要な状況でもありません。エンディアン」)。それは単なる誤解です。
コミュニケーションの崩壊の原因(常に同じですが、正しいです;-)は、単にソースデータの性質について同意していません。文字データをある場所から別の場所に移動するときは、両側にエンコードを指定することが重要です。はい、SQL_Latin1_General_CP1_*
照合順序はコードページ1252を使用します。ただし、BULK INSERT
またはBCP.exeソースファイルのコードページが何であるかを指定しない場合、コードページがシステムのデフォルトであると想定します。
BULK INSERT の状態に関するドキュメント(CODEPAGE =
引数の場合):
'OEM'(デフォルト)= char、varchar、またはtextの列のデータ型は、システムOEMコードページからSQL Serverに変換されますコードページ。
BCP.exe のドキュメントには、-C
スイッチの状態が記載されています。
OEM =クライアントが使用するデフォルトのコードページ。これは、-Cが指定されていない場合に使用されるデフォルトのコードページです。
また、Windowsのデフォルトのコードページは(少なくとも米国英語のシステムでは)437です。コマンドプロンプトで次のコマンドを実行すると、これを確認できます。
C:\> CHCP
戻ります:
Active code page: 437
ただし、ソースファイルはコードページ437を使用してエンコードされていません。コードページ1252を使用してエンコードされています。
だからここに何が起こっているのですか:
É
と表示されます。╔
と表示されますが、BULK INSERT/BCPが表示していないため、これは表示されません)®
として表示)のバイトxAE(コードページ1252では«
)は、バイトxABにマップされます。コードページ1252(«
としても表示されるため)。次の例は、質問に記載されているすべての文字に対するこの変換を示しています。
DECLARE @CodePageConversion TABLE
(
[ActualSource_CP1252] AS CONVERT(VARCHAR(10), CONVERT(BINARY(1),
[PerceivedSource_CP437])) COLLATE SQL_Latin1_General_CP1_CI_AS,
[PerceivedSource_CP437] VARCHAR(10) COLLATE SQL_Latin1_General_CP437_CI_AS,
[Source_Value] AS (CONVERT(BINARY(1), [PerceivedSource_CP437])),
[Destination_CP1252] AS (CONVERT(VARCHAR(10), [PerceivedSource_CP437]
COLLATE SQL_Latin1_General_CP1_CI_AS)),
[CP1252_Value] AS (CONVERT(BINARY(1), CONVERT(VARCHAR(10),
[PerceivedSource_CP437] COLLATE SQL_Latin1_General_CP1_CI_AS)))
);
INSERT INTO @CodePageConversion
VALUES (0xC9), (0xA1), (0xA0), (0xAE), (0xCB), (0xD1), (0x92), (0x96);
SELECT * FROM @CodePageConversion;
これは次を返します:
ActualSource_CP1252 PerceivedSource_CP437 Source_Value Destination_CP1252 CP1252_Value
É ╔ 0xC9 + 0x2B
¡ í 0xA1 í 0xED
á 0xA0 á 0xE1
® « 0xAE « 0xAB
Ë ╦ 0xCB - 0x2D
Ñ ╤ 0xD1 - 0x2D
’ Æ 0x92 Æ 0xC6
– û 0x96 û 0xFB
0xC9、0XCB、および0xD1の文字はコードページ1252に存在しないため、「最適な」マッピングが使用されるため、変換後に+
および-
文字が使用されます。 。
また、宛先列がNVARCHAR
を使用している場合でも、これらのマッピングはすべて同じであるため、まったく同じ動作が見られます。
したがって、選択肢は次のとおりです。
T-SQL BULK INSERT
コマンドを使用する場合は、WITH CODEPAGE =
オプションを次のいずれかの値で指定します。
'ACP'
(これは'1252'
と同じです)'RAW'
(VARCHAR
に挿入する場合は列の照合順序のコードページを使用します。またはNVARCHAR
に挿入する場合は'OEM'
/コードページ437と同じです)'1252'
(これは'ACP'
と同じです)または、BCP.exeを使用する場合、着信ファイルが-C
コマンドラインスイッチを介してコードページ1252を次のいずれかの値とともに使用することを示します(オプション#1の注記を参照) :
ACP
RAW
1252
その点に注意してください:
BULK INSERT
でテストし、VARCHAR
列に挿入し、質問に記載されている文字のセットとACP
(これは[〜 #〜] a [〜#〜] NSI [〜#〜] c [〜#〜] ode [〜#〜] p [〜#〜] age)、RAW
、および1252
の値はすべて正しい結果を生成しました。WITH CODEPAGE =
を指定しないと、O.P。が質問で報告したのと同じ結果が生成されました。これは、WITH CODEPAGE = 'OEM'
を指定するのと同じでした。NVARCHAR
列に挿入すると、ACP
と1252
の両方が希望どおりに機能しましたが、RAW
はOEM
と同じ結果を生成しました(つまり、列の照合順序で指定されるコードページ1252ではなくコードページ437)。-C
スイッチを指定しないと、プロセスのコードページは使用されません。つまり、[〜#〜] chcp [〜#〜]を使用してコマンドを変更するプロンプトのコードページは効果がありませんでした。コードページ437がソースコードページとして引き続き使用されていました。追伸ここのデータはすべて8ビットでエンコードされているため、使用されているUnicodeがないため、「Unicode文字」はありません。
これを修正するには、私は使用しなければなりませんでした:
BULK INSERT table FROM '\\path'
WITH (ERRORFILE = '\\error_path', FIELDTERMINATOR = 'term', DATAFILETYPE = 'widechar')
重要なポイントはDATAFILETYPE = 'widechar'
また、MS Notepadを使用してフラットファイルをユニコードタイプとして保存する必要がありました。
同じ問題があり、フラットファイルをユニコードに変換するためのより良いソリューションが必要な場合は、以下を調査してください https://stackoverflow.com/questions/623330/how-to-convert-txt-file-into-unicode