Warum erlaubt der Datentyp varchar Unicode-Werte?

17

Ich habe eine Tabelle mit einer Varchar-Spalte. Es sind Marken- (™), Copyright- (©) und andere Unicode-Zeichen zulässig (siehe unten).

Create table VarcharUnicodeCheck
(
col1 varchar(100)
)

insert into VarcharUnicodeCheck (col1) values ('MyCompany')
insert into VarcharUnicodeCheck (col1) values ('MyCompany™')
insert into VarcharUnicodeCheck (col1) values ('MyCompany░')
insert into VarcharUnicodeCheck (col1) values ('MyCompanyï')
insert into VarcharUnicodeCheck (col1) values ('MyCompany')

select * from VarcharUnicodeCheck

Die Definition von varchar besagt jedoch, dass Nicht-Unicode-Zeichenfolgendaten zulässig sind. Die Symbole Trademark (™) und Registered (®) sind jedoch Unicode- Zeichen. Widerspricht die Definition der Eigenschaft des Datentyps varchar? Ich habe ein paar Links wie den ersten und den zweiten gelesen . Trotzdem konnte ich nicht verstehen, warum es Unicode-Zeichenfolgen zulässt, wenn die Definition besagt, dass es nur Werte zulässt, die keine Unicode-Zeichenfolgen sind.

Shiva
quelle
12
Alle Zeichen sind Unicode-Zeichen.
Martin Smith
Microsoft verwendet häufig UNICODE, wenn es sich um UTF-16 / UCS-2 handelt. Möglicherweise zählen sie UTF-8 nicht einmal, da UNICODE ein Kontext ist.
CodesInChaos
1
@CodesInChaos: Ich hatte Mühe, Ihren Kommentar zu analysieren, aber ich mache mir Sorgen, dass Sie Unicode mit den verschiedenen UTF-n-Codierungen verwechseln.
Leichtigkeit Rennen mit Monica
1
@ Martin Smith: Wenn alle Zeichen Unicode-Zeichen sind, warum gibt die Microsoft Varchar-Definition dann an , dass Nicht-Unicode-Zeichenfolgendaten zulässig sind?
Shiva
2
Die Codierung für die Zeichen in Varchar ist nicht Unicode, aber alle Zeichen sind in Unicode vorhanden
Martin Smith

Antworten:

15

Die Symbole Trademark (™) und Registered (®) sind jedoch Unicode-Zeichen.

Du liegst hier falsch. Ihre Zeichenfolgen enthalten nur asciiZeichen.

Hier ist ein einfacher Test, der Ihnen zeigt, dass Ihre Charaktere alle ASCII-Zeichen sind (+ einige extended asciimit ASCII-Codes zwischen 128 und 255):

declare @VarcharUnicodeCheck table
(
col1 varchar(100)
)

insert into @VarcharUnicodeCheck (col1) values ('MyCompany')
insert into @VarcharUnicodeCheck (col1) values ('MyCompany™')
insert into @VarcharUnicodeCheck (col1) values ('MyCompany░')
insert into @VarcharUnicodeCheck (col1) values ('MyCompanyï')
insert into @VarcharUnicodeCheck (col1) values ('MyCompany')

select *,
        right(col1, 1)as last_char, 
        ascii(right(col1, 1)) as_last_char_ascii
from @VarcharUnicodeCheck;

Hier können Sie deutlich sehen, dass alle Ihre Zeichen 1-Byte-codiert sind:

Bildbeschreibung hier eingeben

Ja, es handelt sich nicht um reine ASCII- Zeichen, sondern um Extended ASCII .

Hier zeige ich Ihnen echte Unicode-Zeichen Trademark(™)und deren Code und Binärdarstellung:

declare @t table (uni_ch nchar(1), ascii_ch char(1));
insert into @t values (N'™', '™');

select unicode(uni_ch) as [unicode of ™], 
       ascii(ascii_ch) [ascii of ™], 
       cast(uni_ch as varbinary(10)) as [uni_ch as varbinary], 
       cast(ascii_ch as varbinary(10)) as [ascii_ch as varbinary]
from @t;

Bildbeschreibung hier eingeben

Schließlich können Sie sehen, dass das Trademark(™)Unicode-Zeichen 8482-Code und nicht 153 hat:

select nchar(8482), nchar(153)
Sepupic
quelle
1
In dem Artikel, den Sie erwähnt haben, ist jedoch kein "ASCII" -Wort enthalten. Es handelt sich nur um Unicode- und Nicht-Unicode-Zeichen, und Trademark (™), das Sie verwendet haben, war kein Unicode.
Sepupic
16
"Extended ASCII" ist ein schrecklich vieldeutiger Begriff. Es wäre hilfreicher, sich anzuschauen, welche 8-Bit-Codierung tatsächlich verwendet wird (basiert sie auf den Einstellungen für Gebietsschema / Sortierung?). Ich vermute, Windows-Codepage 1252 , die in der Tat ™ als Zeichen 153
codiert
2
@sepupic Ich denke, Sie müssen mehr über den Unterschied zwischen Codepunkten und Codierungen lesen. Wikipedia kann helfen. "Eine Codierung ordnet (möglicherweise eine Teilmenge davon) den Bereich von Unicode- Codepunkten Wertesequenzen in einem Bereich fester Größe zu, die als Codewerte bezeichnet werden ." 8482 ist der Codepunkt für ™, der in Windows-1252 als \ x99 (153), in MacRoman als \ xAA, in UTF-8 als \ xE2 \ x84 \ xA2 usw.
codiert werden kann
7
Bei 8-Bit-Zeichen über 127 ist Vorsicht geboten: Was jeder Code über 127 darstellt, kann und wird sich in Abhängigkeit von der verwendeten Codierung ändern, die in Abhängigkeit von der verwendeten Sortierung variiert. In Codepage 1252 wird Unicode 8482 auf 153 abgebildet. In Codepage 850 wird diese Stelle von 214 ( Ö) eingenommen, und in ISO-8859-1 (manchmal als Latin1 bezeichnet) ist es ein Steuercode ohne druckbare Darstellung. Sofern Sie nicht wissen, dass Sie immer dieselbe Codepage verwenden, ist es sicherer, sich an ANSI-Zeichen (127 oder weniger) zu halten oder Unicode-Typen zu verwenden. Codepage 1252 ist in SQL Server am häufigsten, aber keineswegs allgegenwärtig.
David Spillett
4
@Shiva Das absolute Minimum Jeder Softwareentwickler muss unbedingt über Unicode und Zeichensätze Bescheid wissen . ASCII ist eine Teilmenge vieler Codierungen, und fast alle dieser Codierungen enthalten Nicht-ASCII-Symbole und sind gleichzeitig kein Unicode. Unicode hat auch viele verschiedene Codierungen (wie UTF-8, UTF-32 usw.).
jpmc26
7

Ich stimme den Kommentaren zu, dass "Extended ASCII" ein wirklich schlechter Begriff ist, der eigentlich eine Codepage bedeutet, die Zeichen / Codepunkte im Bereich von 128 bis 255 über den von ASCII definierten Standardbereich von 0 bis 127 Codepunkten hinaus abbildet.

SQL Server unterstützt viele Codepages über Kollatierungen. Nicht-ASCII-Zeichen können in varchar gespeichert werden, solange die zugrunde liegende Kollatierung das Zeichen unterstützt.

Das Zeichen '™' kann in varchar / char-Spalten gespeichert werden, wenn die SQL Server-Sortierungscodeseite 1250 oder höher ist. Die folgende Abfrage listet Folgendes auf:

SELECT COLLATIONPROPERTY(name, 'CodePage') AS code_page, name, description
FROM sys.fn_helpcollations()
WHERE COLLATIONPROPERTY(name, 'CodePage') >= 1250
ORDER BY name;

Nur eine Teilmenge davon unterstützt jedoch auch das Zeichen "©", sodass die Spaltensortierung eine der folgenden sein muss, um beide zu unterstützen:

SELECT COLLATIONPROPERTY(name, 'CodePage') AS code_page, name, description
FROM sys.fn_helpcollations()
WHERE COLLATIONPROPERTY(name, 'CodePage') IN(
    1250
    ,1251
    ,1252
    ,1253
    ,1254
    ,1255
    ,1256
    ,1257
    ,1258
)
ORDER BY name;
Dan Guzman
quelle
4

Die Definition von varchar besagt jedoch, dass Nicht-Unicode-Zeichenfolgendaten zulässig sind . Die Symbole Trademark (™) und Registered (®) sind jedoch Unicode- Zeichen . Widerspricht die Definition der Eigenschaft des Datentyps varchar?

Die anderen Antworten sind zwar nicht falsch, aber ich denke, es würde helfen, auf eine Verwirrung in der Basisterminologie hinzuweisen. Ich habe im obigen Zitat zwei Wörter aus der Frage als Beispiel für diese Verwirrung hervorgehoben. Wenn in der SQL Server-Dokumentation von Unicode- und Nicht-Unicode- Daten die Rede ist , handelt es sich nicht um die Zeichen . Sie sprechen von den Bytefolgen, die bestimmte Zeichen darstellen. Der Hauptunterschied zwischen den Unicode - Typen ( NCHAR, NVARCHAR, XML, und die veralteten / Übel NTEXT) und dem nicht-Unicode - Typen ( CHAR, VARCHARund die veralteten / Übel TEXT) ist , was Arten von Bytefolgen sie speichern können.

Die Nicht-Unicode-Typen speichern eine von mehreren 8-Bit-Codierungen, während die Unicode-Typen eine einzige 16-Bit-Unicode-Codierung speichern: UTF-16 Little Endian. Wie die anderen Antworten bereits erwähnt haben, hängt es von der Codepage ab, die durch die Sortierung bestimmt wird, welche Zeichen in einer 8-Bit- / Nicht-Unicode-Codierung gespeichert werden können. Während andere festgestellt haben, dass der Bytewert eines "Zeichens" von Codepage zu Codepage variieren kann, kann der Bytewert sogar innerhalb derselben Codepage variieren, wenn es sich um eine der mehreren EBCDIC-Codepages handelt (Variationen von Windows-Codepages). 1252), die nur in älteren Versionen zu finden sind, sollten nicht unbedingt für SQL Server-Kollatierungen verwendet werden (dh solche mit Namen, die mit beginnen SQL_).

Daher ist die Definition korrekt: Alle Zeichen, die Sie in einem Nicht-Unicode-Typ speichern können, sind immer 8-Bit-Zeichen (auch wenn sie zwei 8-Bit-Werte in Kombination als ein einziges "Zeichen" verwenden). Byte Character Set / DBCS-Codepages berücksichtigen). Und die Unicode-Datentypen sind immer 16-Bit, auch wenn sie manchmal zwei 16-Bit-Werte in Kombination als ein einzelnes "Zeichen" verwenden (dh ein Ersatzpaar, das wiederum ein Zusatzzeichen darstellt).

UND, da SQL Server die UTF-8-Codierung für VARCHARund CHARDatentypen ab SQL Server 2019 nativ unterstützt ,

VARCHARkann nicht mehr als "Nicht-Unicode" bezeichnet werden. Ab der ersten öffentlichen Betaversion von SQL Server 2019 im September 2018 sollten wir von VARCHAReinem "8-Bit-Datentyp" sprechen, auch wenn es sich um Versionen vor SQL Server 2019 handelt. Diese Terminologie gilt für alle vier Typen von Kodierungen, die verwendet werden können mit VARCHAR:

  1. Erweitertes ASCII
  2. Doppelbyte-Zeichensätze (DBCS)
  3. EBCDIC
  4. UTF-8 (Unicode)

Lediglich der TEXTDatentyp (der ab SQL Server 2005 veraltet ist, verwenden Sie ihn also nicht mehr) ist "Nicht-Unicode", dies ist jedoch nur eine technische Angelegenheit, und es ist korrekt, ihn als "8-Bit-Datentyp" zu bezeichnen.

NVARCHAR, NCHARUnd NTEXTkann als „UTF-16“ oder „16-Bit - Datentyp“ bezeichnet werden. Ich glaube, Oracle verwendet die Terminologie von "Unicode-only" für NVARCHAR, aber das schließt nicht eindeutig die Möglichkeit aus, UTF-8 (auch eine Unicode-Codierung) zu verwenden, die nicht funktioniert die ersten beiden Möglichkeiten.

Einzelheiten zu den neuen UTF-8-Codierungen finden Sie in meinem Beitrag:

Native UTF-8-Unterstützung in SQL Server 2019: Retter oder falscher Prophet?

PS Ich arbeite mich langsam durch die Aktualisierung der SQL Server-Dokumentation, um diese Änderungen zu berücksichtigen.

PPS Microsoft hat bereits einige Seiten mit UTF-8-Informationen aktualisiert, einschließlich der Char- und Varchar- Dokumentation, auf die in der Frage verwiesen wird. Es enthält nicht länger die Phrase "non-Unicode". Aber das ist nur eine FYI; Dies ändert nichts an der Frage, da es sich um Nicht-Unicode-Codierungen handelt, die Zeichen enthalten, die fälschlicherweise als reine Unicode-Codierungen angesehen wurden.

Solomon Rutzky
quelle
3

Die Frage enthält ein zentrales Missverständnis darüber, was Unicode ist. Der Unicode-Zeichensatz ist zusammen mit seinen Codierungen wie UTF-8 und UTF-16 eine von vielen Möglichkeiten, Text in einem Computer darzustellen. Ziel ist es, alle anderen Zeichensätze und Codierungen zu ersetzen. Wenn "Nicht-Unicode-Daten" "Zeichen, die in Unicode nicht vorhanden sind" bedeuten, könnte keiner der in dieser Antwort verwendeten Texte in diesem Typ gespeichert werden, da alle Buchstaben des lateinischen Alphabets und die im Englischen üblichen Interpunktionen verwendet werden in Unicode enthalten.

Textdarstellungen können grob in zwei Teilen betrachtet werden: einem Zeichensatz, der die verschiedenen Zeichen (Buchstaben, Ziffern, Symbole usw.) auf einem Referenzdiagramm den Zahlen zuordnet; und eine Codierung, die diese Zahlen als Bitmuster darstellt (auf der Festplatte, über eine Netzwerkverbindung usw.). Hier geht es hauptsächlich um den ersten Teil: Welche Zeichen sind in den Diagrammen für einen bestimmten Zeichensatz aufgeführt?

Da Unicode für jedes Zeichen auf der Welt Zahlen (die als "Codepunkte" bezeichnet werden) anstrebt, beziehen sich Verweise wie Wikipedia häufig auf die Unicode-Position eines Zeichens als Standard-Referenzinformation. Dies bedeutet jedoch nicht, dass andere Zeichensätze keine Zuordnung für dasselbe Zeichen haben.

Einer der ältesten und einfachsten Zeichensätze (und Kodierungen), die noch verwendet werden, ist ASCII, das Zuordnungen für 128 verschiedene Zeichen (0 bis 127) enthält, da jedes Zeichen mit 7 Bits kodiert wird. Seit diesem schließen viele akzentuierten Zeichen und Symbolen gemeinsamer Verwenden später Codierungen 8 Bits, und ordnen die gleichen ersten 128 Zeichen, zusätzlich zu dem Zeichensatz von Positionen 128 bis 255. Bemerkenswert unter diesen sind Füllung der Standard ISO 8859-1 und ISO 8859- 15 und den Microsoft-spezifischen Windows-Code Seite 1252 .

Zurück zu MS SQL Server: Eine "Unicode-Zeichenfolge", wie sie in einer nchar, nvarcharoder ntextSpalte gespeichert ist , kann alle im Unicode-Zeichensatz zugeordneten Zeichen darstellen, da zum Speichern der Daten eine Unicode-Codierung verwendet wird. Eine "Nicht-Unicode-Zeichenfolge", wie sie in einer char, varcharoder textSpalte gespeichert ist , kann nur die Zeichen darstellen, die in einer anderen Codierung zugeordnet sind . Alles, was Sie in einer Nicht-Unicode-Spalte speichern können, kann auch in einer Unicode-Spalte gespeichert werden, aber nicht umgekehrt.

Um genau zu wissen, welche Zeichen Sie speichern können, müssen Sie die verwendete "Kollatierung" kennen, die vorgibt, was Microsoft als "Codepage" bezeichnet, wie auf dieser Microsoft-Referenzseite erläutert . In Ihrem Fall verwenden Sie wahrscheinlich den sehr verbreiteten Code, den ich bereits erwähnt habe.

Die von Ihnen genannten Zeichen sind sowohl in Unicode als auch in Code Page 1252 enthalten:

  • Das Warenzeichen (TM) erscheint in Unicode an Position 8482 und in CP1252 an Position 153
  • Dabei erscheint Registered (®) sowohl in Unicode als auch in CP1252 an Position 174
IMSoP
quelle
3
"Unicode ist eine von vielen Möglichkeiten, Text für die Verwendung in einem Computer zu codieren" - das ist nicht korrekt. Unicode ist nur eine Sammlung von Zeichen und Symbolen, wobei jedes Zeichen einen eigenen eindeutigen Codepunkt hat, der nur eine Zahl ist. Die Aufgabe einer Codierung besteht dann darin, diese Codepunkte mit einer Bytefolge abzugleichen. UTF-8 und UTF-16 sind Codierungen, Unicode nicht.
Poke
@poke Wie ich in der Antwort weiter ausführen werde, verwende ich hier "Codierung", um sowohl "Zuordnung von Zeichen zu Positionen in einem Diagramm" als auch "Darstellungen dieser Positionen als Folge von Bits" darzustellen. Vielleicht gibt es einen besseren Begriff, aber ich bin mir nicht sicher, wie er lauten würde.
IMSoP
3
Nun, Sie können nicht einfach "Kodierung" mit Ihrer eigenen Definition verwenden. Es tut uns leid, dass Sie hier nicht picken, aber Sie können das nicht in einer Antwort tun, die mit "Die Frage enthält ein zentrales Missverständnis darüber, was Unicode ist" beginnt .
Poke
2
IMSoP (und @poke): Ich stimme Poke in Bezug auf die Überschreitung der Reichweite bei der Verwendung von "Kodierung" zu, was etwas anderes als Kodierung bedeutet, obwohl ich auch mit dem Dilemma von IMSoP einverstanden bin. Ich bevorzuge es, Unicode als Zeichensatz mit mehreren Codierungen zu bezeichnen, während Zeichensatz und Codierung in der Regel austauschbar sind, da sie die meiste Zeit (oder vielleicht alle?) Eine 1-zu-1-Beziehung bilden.
Solomon Rutzky
2
Gute Antwort. Ich empfehle dringend, einen Link zu " Das absolute Minimum" hinzuzufügen, den jeder Softwareentwickler unbedingt über Unicode und die darin enthaltenen Zeichensätze wissen muss .
jpmc26