"CO2" kann in der Tabellenzeile nicht auf "CO₂" aktualisiert werden


19

Angesichts dieser Tabelle:

CREATE TABLE test (
    id INT NOT NULL,
    description NVARCHAR(100) COLLATE Modern_Spanish_CI_AS NOT NULL
);
INSERT INTO test (id, description) VALUES (1, 'CO2');

Ich habe festgestellt, dass ich ein typografisches Problem nicht beheben kann:

SELECT * FROM test WHERE id = 1;
UPDATE test SET description = 'CO₂' WHERE id = 1;
SELECT * FROM test WHERE id = 1;

Da das Update zwar passt, aber keine Auswirkung hat:

id          description
----------- -----------
1           CO2

(1 affected rows)

(1 affected rows)

id          description
----------- -----------
1           CO2

(1 affected rows)

Es ist, als ob SQL Server feststellt, dass sich der endgültige Wert nicht ändert , da offensichtlich nur eine winzige 2 ist , sodass es sich nicht lohnt, ihn zu ändern.

Könnte jemand etwas Licht ins Dunkel bringen und vielleicht eine Problemumgehung vorschlagen (außer die Aktualisierung auf einen Zwischenwert)?


1
Álvaro: Wenn Sie mehr über dieses Verhalten erfahren möchten, um besser zu verstehen, warum dies passiert ist, lesen Sie bitte die beiden Links, die ich gerade am Ende meiner Antwort hinzugefügt habe.
Solomon Rutzky

Antworten:


29

Der Index 2 ist nicht Teil des varchar-Zeichensatzes (nicht nur Modern_Spanish). Machen Sie es also zu einer nvarchar-Konstante:

UPDATE test SET description = N'CO₂' WHERE id = 1;

1
Ich habe nicht nur den Wert festgelegt, sondern auch verstanden, wie er überhaupt dort angekommen ist. Vielen Dank!
Álvaro González

2
@ ÁlvaroGonzález und gbn: Aus Gründen der Übersichtlichkeit ist "Subscript 2" in der Codepage nicht verfügbar, die in der Standardkollation der betreffenden Datenbank angegeben ist. Hierbei handelt es sich um die Kollation, die für Zeichenfolgenliterale und -variablen verwendet wird, und nicht um die Kollation der Spalte (obwohl beides) könnte dieselbe Codepage verwenden). "Index 2" ist jedoch in Codepage 949 über die Korean Collations verfügbar. Das wird hier nicht helfen, aber nur zu Ihrer Information. Ich habe Details und ein Beispiel in meiner Antwort .
Solomon Rutzky

21

@gbn hat bereits den Grund und die Fehlerbehebung erklärt, aber der spezifische Grund für das Verhalten, das Sie sehen, ist folgender:

  1. Sie verwenden ein VARCHARLiteral (kein NPräfix) anstelle eines NVARCHARLiteral (Zeichenfolge mit NPräfix), daher wird das Unicode-Zeichen in konvertiert VARCHAR.
  2. VARCHARist eine 8-Bit-Codierung, die in den meisten Fällen aus einem Byte pro Zeichen besteht, aber auch aus zwei Byte pro Zeichen bestehen kann. Auf der anderen Seite NVARCHARhandelt es sich um eine 16-Bit-Codierung (UTF-16 Little Endian), die entweder aus zwei oder vier Bytes pro Zeichen besteht.
  3. Aufgrund der unterschiedlichen Anzahl verfügbarer Bytes für die Zuordnung von Zeichen ist die Anzahl der zuzuordnenden Zeichen bei 8-Bit-Codierungen naturgemäß wesentlich eingeschränkter. VARCHARDaten sind bis zu 256 Zeichen für Einzelbyte-Zeichensätze (die meisten von ihnen) und bis zu 65.536 Zeichen für Doppelbyte-Zeichensätze (nur einige davon). Andererseits können NVARCHARDaten etwas mehr als 1,1 Millionen Unicode-Zeichen abbilden (obwohl derzeit knapp 250.000 zugeordnet sind).
  4. Aufgrund der begrenzten Anzahl von Zuordnungen, die mit 8-Bit / VARCHARDaten durchgeführt werden können, werden verschiedene Gruppierungen von Zeichen (basierend auf Sprache / Kultur) auf mehrere "Codeseiten" (dh Zeichensätze) verteilt.
  5. Jede Kollatierung gibt an, welche Codepage (falls vorhanden) für VARCHARDaten verwendet werden soll ( NVARCHARsind alle Zeichen).
  6. Beim Konvertieren eines Zeichenfolgenliteral oder einer Variablen von NVARCHAR(z. B. Unicode / UTF-16 / alle Zeichen) in VARCHAR(Zeichensatz basierend auf der Codepage, die in den meisten Sortierungen angegeben ist) wird die Standard-Sortierung der Datenbank verwendet
  7. Wenn die für die Konvertierung verwendete Codepage der Kollatierung nicht dasselbe Zeichen enthält, sondern eine Zuordnung "Beste Übereinstimmung" enthält, wird die Zuordnung "Beste Übereinstimmung" verwendet.
  8. Wenn die für die Konvertierung verwendete Codepage der Kollatierung nicht dasselbe Zeichen oder eine Zuordnung "Beste Anpassung" enthält, wird das Standardzeichen "Ersetzung" verwendet (am häufigsten ?).

Also, was Sie sehen , ist eine NVARCHARzu VARCHARUmwandlung aufgrund des fehlenden NPräfix auf dem Stringliteral. Die Codepage der Standardkollation für die Datenbank enthält nicht genau dasselbe Zeichen, es wurde jedoch eine Zuordnung "Beste Übereinstimmung" gefunden. Aus diesem Grund wird eine 2anstelle einer Zuordnung angezeigt ?.

Sie können diesen Effekt sehen, indem Sie den folgenden einfachen Test durchführen:

SELECT '₂', N'₂';

Kehrt zurück:

2    ₂

Wenn die Codepage der Standardkollation für die Datenbank genau dasselbe Zeichen enthielte, hätte dies die Codepage in dasselbe Zeichen übersetzt. In Ihrem Fall hätte es dann, da Sie in einer NVARCHARSpalte speichern , wieder in das ursprüngliche Unicode-Zeichen übersetzt. Das letzte Beispiel unten zeigt dieses Verhalten.

WICHTIG: Beachten Sie, dass die Konvertierung stattfindet, während das Zeichenfolgenliteral interpretiert wird, bevor es in der Spalte gespeichert wird. Dies bedeutet, dass die Spalte, auch wenn sie dieses Zeichen enthalten kann, auf der Grundlage der Standardkollatierung der Datenbank bereits in etwas anderes konvertiert wurde, da das NPräfix in diesem Zeichenfolgenliteral weggelassen wurde. Und genau das erleben Sie (oder waren Sie).

Wenn die Standardkollatierung Ihrer Datenbank beispielsweise eine der koreanischen Kollatierungen gewesen wäre (einer der vier Doppelbyte-Zeichensätze), hätten Sie dieses Problem nicht gesehen, da das Zeichen "Index 2" in diesem Zeichen verfügbar ist gesetzt (Code Seite 949). Probieren Sie den folgenden Test aus (anstelle der Standardkollatierung der Datenbank wird die Kollatierung der Spalte verwendet, da dies einfacher anzuzeigen ist):

CREATE TABLE #TestChar
(
    [8bit_Latin1_General-1252] VARCHAR(2) COLLATE Latin1_General_100_CI_AS_SC,
    [8bit_Korean-949] VARCHAR(2) COLLATE Korean_100_CI_AS_SC,
    [UTF16LE_Latin1_General-1252] NVARCHAR(2) COLLATE Latin1_General_100_CI_AS_SC
);

INSERT INTO #TestChar VALUES (N'₂', N'₂', N'₂');

SELECT * FROM #TestChar;

Kehrt zurück:

8bit_Latin1_General-1252    8bit_Korean-949    UTF16LE_Latin1_General-1252
2                           ₂                  ₂

Wie Sie sehen, haben die Latin1_General-Kollatierungen, die Codepage 1252 (dieselbe Codepage wie die Modern_SpanishKollatierungen) für VARCHARDaten verwenden, keine exakte Übereinstimmung, aber sie haben eine Zuordnung "am besten geeignet" (wie Sie sehen) ). ABER die koreanischen Kollatierungen, die Code Page 949 für VARCHARDaten verwenden, stimmen genau mit dem Zeichen "Index 2" überein.


Zur weiteren Veranschaulichung können wir eine neue Datenbank mit einer Standard-Kollatierung einer der koreanischen Kollatierungen erstellen und dann genau die SQL ausführen, die in Frage kommt:

CREATE DATABASE [TestKorean-949] COLLATE Korean_100_CI_AS_KS_WS_SC;
ALTER DATABASE [TestKorean-949] SET RECOVERY SIMPLE;
GO

USE [TestKorean-949];

CREATE TABLE test (
    id INT NOT NULL,
    description NVARCHAR(100) COLLATE Modern_Spanish_CI_AS NOT NULL
);
INSERT INTO test (id, description) VALUES (1, 'CO2');


SELECT * FROM test WHERE id = 1;
UPDATE test SET description = 'CO₂' WHERE id = 1;
SELECT * FROM test WHERE id = 1;

Kehrt zurück:

id  description
1   CO2


id  description
1   CO₂

AKTUALISIEREN

Für alle, die mehr darüber erfahren möchten, was genau hier vor sich geht (dh alle wichtigen Details), lesen Sie bitte die zweiteilige Untersuchung, die ich gerade gepostet habe:

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.