it-swarm.com.de

SQL Server Bulk Insert interpretiert einige Unicode-Zeichen richtig, andere jedoch nicht?

Aus irgendeinem Grund interpretiert/übersetzt die Masseneinfügung von MS SQL Server 2016 Unicode-Zeichen falsch:

  • C9 (É) in 2B (+)
  • A1 (¡) in ED (í)
  • A0 () in E1 (á)
  • AE (®) in AB (")
  • CB (Ë) in 2D (-)
  • D1 (Ñ) in 2D (-)
  • 92 (’) in C6 (Æ)
  • 96 (-) in FB (û)

d.h. Notepad ++ und xxd zeigen, dass die flache Datei 0xC9 hat, aber nach dem Masseneinfügen zeigt die Tabelle "+" und die Umwandlung als varbinary in SQL Server zeigt sie als 0x2B an. Das Backup hat auch 0xC9.

Ich füge 25 flache Dateien als Masseneinfügung in MS SQL Server 2016 ein. Es sind 15-GB-Daten und ich verwende ein Pipe-Feldtrennzeichen () und CRLF Zeilenbegrenzer.

Ich füge Bulk-Insert in die abgeschnittene Struktur eines Backups ein, das mir zur Verfügung gestellt wird. Und wenn ich mit dem Backup vergleiche, gibt es Unterschiede. HINWEIS: Ich muss 25 Stunden auf das Backup von der Datenquelle warten, kann aber die Flatfiles in 15 Minuten erhalten.

Einige Unterschiede sind akzeptabel (Suchen und Ersetzen bewerbe ich mich um die Flatfiles), aber viele sind darauf zurückzuführen, dass Unicode-Zeichen falsch interpretiert werden.

Die Struktur einer Beispieltabelle lautet:

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]

Die Datenbanksortierung ist Standard: SQL_Latin1_General_CP1_CI_AS. Keine Spalten haben eine andere Sortierung. Diese Zusammenstellung sollte die Codepage 1252 verwenden, die die Zeichen, mit denen ich Probleme habe, richtig interpretieren sollte.

Mein Prozess wird mit Produktionsdaten ausgeführt, die ständig im Fluss sind. Daher befürchte ich, dass andere Änderungen auftreten könnten, und ich möchte die Ursache des Problems kennen, anstatt zu versuchen, das Problem zu isolieren und die Fehlinterpretationen manuell zu aktualisieren.

5
Dave Goldsmith

Dies ist weder ein Fehler in SQL Server (oder sogar in Windows) noch eine Situation, die den zusätzlichen Schritt des Konvertierens der Datei in eine andere Codierung erfordert (dh in "Unicode", was in Windows-world "UTF-16 Little" bedeutet Endian "). Es ist nur eine einfache Fehlkommunikation.

Die Quelle der Kommunikationsstörung (es ist immer die gleiche, richtig ;-) stimmt lediglich nicht mit der Art der Quelldaten überein. Wenn Sie Zeichendaten von einem Ort an einen anderen verschieben, ist es wichtig, die Codierung auf beiden Seiten anzugeben. Ja, die SQL_Latin1_General_CP1_* - Kollatierungen verwenden Codepage 1252. Wenn Sie jedoch BULK INSERT Oder BCP.exe nicht mitteilen, wie die Codepage der Quelldatei lautet, dann nehmen sie an, dass die Codepage die Systemvorgabe ist.

In der Dokumentation zu BULK INSERT gilt sogar (für das Argument CODEPAGE =):

'OEM' (Standard) = Spalten mit dem Datentyp char, varchar oder text ​​werden von der System-OEM-Codepage in den SQL Server konvertiert Codepage.

In der Dokumentation zu BCP.exe heißt es (für den Schalter -C):

OEM = Vom Client verwendete Standardcodepage. Dies ist die Standardcodepage, die verwendet wird, wenn -C nicht angegeben ist.

Die Standard-Codepage für Windows lautet (zumindest für US-englische Systeme): 437. Sie können dies sehen, wenn Sie in einer Eingabeaufforderung Folgendes ausführen:

C:\> CHCP

Es wird zurückkehren:

Active code page: 437

Ihre Quelldatei wurde jedoch nicht mit Code Page 437 codiert. Sie wurde mit Code Page 1252 codiert.

Also hier ist was passiert:

  1. Bytes sind Bytes. Und Bytes, die Zeichendaten darstellen, können nur über eine Codierung interpretiert werden. Alles, was eine Datei liest, liest keine Zeichen aus der Datei, sondern die Bytes der Datei und zeigt die Zeichen basierend auf der angegebenen Codierung an.
  2. BULK INSERT/BCP liest Byte xC9 ein. xC9 wird bei Verwendung von Code Page 1252 als É Angezeigt.
  3. BULK INSERT/BCP erhält keine Quellcodepage, überprüft also die aktuelle Codepage für den Prozess und erhält folgende Meldung: 437.
  4. BULK INSERT/BCP hat jetzt ein Byte von xC9 für Codepage 437 (das als Angezeigt wird, aber BULK INSERT/BCP zeigt es nicht so an du wirst das nicht sehen)
  5. BULK INSERT/BCP fügt diese Daten mithilfe einer Kollatierung, die Code Page 1252 angibt, in eine Spalte ein.
  6. SQL Server erkennt, dass die eingehenden Daten eine andere Codepage verwenden als das Ziel und muss daher die eingehenden Daten so konvertieren, dass die Zeichen (so weit wie möglich) gleich erscheinen, auch wenn sich die zugrunde liegenden Werte ändern.
  7. Die Zuordnung von Code Page 437 zu Code Page 1252 gibt an, dass Byte xC9 Byte x2B zugeordnet ist. In ähnlicher Weise wird Byte xAE (das ist ® Auf Codepage 1252) auf Codepage 437 (das als « Angezeigt wird) Byte xAB zugeordnet = auf Code Page 1252 (weil es auch als « angezeigt wird).

Das folgende Beispiel zeigt diese Konvertierung für alle in der Frage angegebenen Zeichen:

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;

was zurückgibt:

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

Die Zeichen für 0xC9, 0XCB und 0xD1 sind in Code Page 1252 nicht vorhanden, daher werden die Zuordnungen "am besten passen" verwendet, weshalb Sie die Zeichen + Und - Nachher erhalten die Umwandlung.

Selbst wenn die Zielspalte NVARCHAR verwendet, sind alle diese Zuordnungen gleich, sodass Sie genau dasselbe Verhalten sehen würden.

Sie haben also die Wahl:

  1. Wenn Sie den T-SQL-Befehl BULK INSERT Verwenden, geben Sie die Option WITH CODEPAGE = Mit einem der folgenden Werte an:

    1. 'ACP' (Dies ist dasselbe wie '1252')
    2. 'RAW' (Dies verwendet die Codepage der Sortierung der Spalte beim Einfügen in VARCHAR oder entspricht 'OEM'/Code Page 437 beim Einfügen in NVARCHAR )
    3. '1252' (Dies ist dasselbe wie 'ACP')
  2. Wenn Sie BCP.exe verwenden, geben Sie an, dass die eingehende Datei Code Page 1252 über den Befehlszeilenschalter -C Zusammen mit einem der folgenden Werte verwendet (siehe Hinweise in Option 1) ):

    1. ACP
    2. RAW
    3. 1252

Bitte beachte, dass:

  1. Ich habe mit BULK INSERT Getestet und in eine VARCHAR - Spalte eingefügt, wobei ich den in der Frage angegebenen Zeichensatz und das ACP (von dem ich glaube, dass es für [steht) verwendet habe. ~ # ~] a [~ # ~] NSI C ode P = Alter), RAW und 1252 führten alle zu den richtigen Ergebnissen.
  2. Wenn Sie WITH CODEPAGE = Nicht angeben, erhalten Sie die gleichen Ergebnisse, die der O.P. in der Frage angegeben hat. Dies war dasselbe wie die Angabe von WITH CODEPAGE = 'OEM'.
  3. Beim Einfügen in eine NVARCHAR -Spalte funktionierten sowohl ACP als auch 1252 Wie gewünscht, aber RAW ergab das gleiche Ergebnis wie OEM (dh Verwenden von Code Page 437 anstelle von Code Page 1252, die durch die Sortierung der Spalte angegeben wird.
  4. Ich habe mit BCP.exe getestet und ohne Angabe des Schalters -C nicht die Codepage des Prozesses verwendet. Das heißt, die Verwendung von CHCP zum Ändern des Befehls Die Codepage der Eingabeaufforderung hatte keine Auswirkung: Codepage 437 wurde weiterhin als Quellcodepage verwendet.

P.S. Da die Daten hier alle 8-Bit-codiert sind, gibt es keine "Unicode-Zeichen", da kein Unicode verwendet wird.

7
Solomon Rutzky

Um dies zu beheben, musste ich verwenden:

BULK INSERT table FROM '\\path'
WITH (ERRORFILE = '\\error_path', FIELDTERMINATOR = 'term', DATAFILETYPE = 'widechar')

Der entscheidende Punkt ist DATAFILETYPE = 'widechar'

Und ich musste die Flatfile mit MS Notepad als Typ Unicode speichern.

Wenn Sie dasselbe Problem haben und eine bessere Lösung für die Konvertierung von Flatfiles in Unicode benötigen, untersuchen Sie Folgendes: https://stackoverflow.com/questions/623330/how-to-convert-txt-file-into-unicode

0
Dave Goldsmith