Wie ein "Zeichen" (das aus mehreren Codepunkten bestehen kann: Ersatzpaare, Kombinieren von Zeichen usw.) mit einem anderen verglichen wird, basiert auf einem ziemlich komplexen Regelwerk. Es ist so komplex, weil all die verschiedenen (und manchmal "verrückten") Regeln berücksichtigt werden müssen, die in allen in der Unicode- Spezifikation dargestellten Sprachen zu finden sind. Dieses System gilt für nicht-binäre Kollatierungen für alle NVARCHAR
Daten und für VARCHAR
Daten, die eine Windows-Kollatierung und keine SQL Server-Kollatierung verwenden (eine beginnend mit SQL_
). Dieses System gilt nicht für VARCHAR
Daten, die eine SQL Server-Kollatierung verwenden, da diese einfache Zuordnungen verwenden.
Die meisten Regeln sind im Unicode Collation Algorithm (UCA) definiert . Einige dieser Regeln und einige, die nicht in der UCA enthalten sind, sind:
- Die in der
allkeys.txt
Datei angegebene Standardreihenfolge / das Standardgewicht (siehe unten)
- Welche Sensitivitäten und Optionen werden verwendet (zB ist es case sensitive oder insensitive? Und wenn sensitiv, ist es zuerst groß oder zuerst klein?)
- Alle auf dem Gebietsschema basierenden Überschreibungen.
- Die Version des Unicode-Standards wird verwendet.
- Der Faktor "Mensch" (dh Unicode ist eine Spezifikation, keine Software, und es ist daher jedem Anbieter überlassen, sie zu implementieren)
Ich habe diesen letzten Punkt in Bezug auf den menschlichen Faktor betont, um hoffentlich klar zu machen, dass man nicht erwarten sollte, dass sich SQL Server immer zu 100% gemäß der Spezifikation verhält.
Der übergeordnete Faktor hierbei ist die Gewichtung jedes Codepunkts und die Tatsache, dass mehrere Codepunkte dieselbe Gewichtungsspezifikation haben können. Die Grundgewichte (keine länderspezifischen Überschreibungen) finden Sie hier (ich glaube, die 100
Kollatierungsserie ist Unicode v 5.0 - informelle Bestätigung in den Kommentaren zum Microsoft Connect-Element ):
http://www.unicode.org/Public/UCA/5.0.0/allkeys.txt
Der betreffende Codepunkt - U + FFFD - ist wie folgt definiert:
FFFD ; [*0F12.0020.0002.FFFD] # REPLACEMENT CHARACTER
Diese Notation ist in Abschnitt 9.1 Allkeys-Dateiformat der UCA definiert:
<entry> := <charList> ';' <collElement>+ <eol>
<charList> := <char>+
<collElement> := "[" <alt> <weight> "." <weight> "." <weight> ("." <weight>)? "]"
<alt> := "*" | "."
Collation elements marked with a "*" are variable.
Diese letzte Zeile ist wichtig, da der Code Point, den wir betrachten, eine Spezifikation hat, die in der Tat mit "*" beginnt. In Abschnitt 3.6 Variable Gewichtung werden vier mögliche Verhaltensweisen definiert, die auf den Kollatierungskonfigurationswerten basieren, auf die wir keinen direkten Zugriff haben (diese sind in der Microsoft-Implementierung jeder Kollatierung fest codiert, z. B. ob bei Groß- und Kleinschreibung zuerst Kleinbuchstaben verwendet werden oder zuerst in Großbuchstaben, eine Eigenschaft, die sich zwischen VARCHAR
Daten, die SQL_
Kollatierungen verwenden, und allen anderen Variationen unterscheidet).
Ich habe keine Zeit, mich eingehend mit der Frage zu befassen, welche Pfade eingeschlagen werden und welche Optionen verwendet werden, damit ein sicherer Beweis erbracht werden kann, aber man kann mit Sicherheit sagen, dass in jeder Codepunktspezifikation unabhängig davon, ob dies der Fall ist oder nicht gilt als "gleich" wird nicht immer die vollständige Spezifikation verwenden. In diesem Fall haben wir "0F12.0020.0002.FFFD" und höchstwahrscheinlich werden nur die Ebenen 2 und 3 verwendet (dh .0020.0002. ). Ausführen einer "Zählung" in Notepad ++ für ".0020.0002" findet 12.581 Übereinstimmungen (einschließlich zusätzlicher Zeichen, mit denen wir uns noch nicht befasst haben). Wenn Sie bei "[*" eine "Zählung" durchführen, werden 4049 Übereinstimmungen zurückgegeben. Durchführen eines RegEx "Find" / "Count" unter Verwendung eines Musters von\[\*\d{4}\.0020\.0002
liefert 832 Treffer. Irgendwo in dieser Kombination, plus möglicherweise einigen anderen Regeln, die ich nicht sehe, plus einigen Microsoft-spezifischen Implementierungsdetails, ist die vollständige Erklärung dieses Verhaltens. Und um klar zu sein, ist das Verhalten für alle übereinstimmenden Zeichen gleich, da alle übereinstimmenden Zeichen das gleiche Gewicht haben, sobald die Regeln angewendet wurden (das heißt, diese Frage hätte zu jedem von ihnen gestellt werden können, nicht zu jedem unbedingt Mr. �
).
Sie können mit der folgenden Abfrage und dem Ändern der COLLATE
Klausel anhand der Ergebnisse unter der Abfrage sehen, wie die verschiedenen Sensitivitäten in den beiden Versionen von Collations funktionieren:
;WITH cte AS
(
SELECT TOP (65536) ROW_NUMBER() OVER (ORDER BY (SELECT 0)) - 1 AS [Num]
FROM [master].sys.columns col
CROSS JOIN [master].sys.objects obj
)
SELECT cte.Num AS [Decimal],
CONVERT(VARBINARY(2), cte.Num) AS [Hex],
NCHAR(cte.Num) AS [Character]
FROM cte
WHERE NCHAR(cte.Num) = NCHAR(0xFFFD) COLLATE Latin1_General_100_CS_AS_WS --N'�'
ORDER BY cte.Num;
Die Anzahl der übereinstimmenden Zeichen bei verschiedenen Sortierungen ist unten angegeben.
Latin1_General_100_CS_AS_WS = 5840
Latin1_General_100_CS_AS = 5841 (The "extra" character is U+3000)
Latin1_General_100_CI_AS = 5841
Latin1_General_100_CI_AI = 6311
Latin1_General_CS_AS_WS = 21,229
Latin1_General_CS_AS = 21,230
Latin1_General_CI_AS = 21,230
Latin1_General_CI_AI = 21,537
In allen oben aufgelisteten Kollatierungen wird N'' = N'�'
ebenfalls true ausgewertet.
AKTUALISIEREN
Ich konnte ein bisschen mehr recherchieren und hier ist, was ich gefunden habe:
Wie es "wahrscheinlich" funktionieren soll
Mit der ICU Collation Demo habe ich das Gebietsschema auf "en-US-u-va-posix" gesetzt, die Stärke auf "primary" gesetzt, "sort keys" (Sortierschlüssel anzeigen) markiert und die folgenden 4 Zeichen eingefügt, die ich aus der kopiert habe Ergebnisse der obigen Abfrage (unter Verwendung der Latin1_General_100_CI_AI
Kollatierung):
�
Ԩ
ԩ
Ԫ
und das gibt zurück:
Ԫ
60 2E 02 .
Ԩ
60 7A .
ԩ
60 7A .
�
FF FD .
Überprüfen Sie dann die Zeicheneigenschaften für " " unter http://unicode.org/cldr/utility/character.jsp?a=fffd und stellen Sie FF FD
sicher, dass der Sortierschlüssel der Ebene 1 (dh ) mit der Eigenschaft "uca" übereinstimmt. Durch Klicken auf diese Eigenschaft "uca" gelangen Sie zu einer Suchseite - http://unicode.org/cldr/utility/list-unicodeset.jsp?a=%5B%3Auca%3DFFFD%3A%5D - mit nur 1 Treffer. In der Datei allkeys.txt wird das Sortiergewicht der Ebene 1 als angezeigt 0F12
, und es gibt nur eine Übereinstimmung dafür.
Um sicherzustellen, dass wir das Verhalten richtig interpretieren, habe ich mir ein anderes Zeichen angesehen: GREEK CAPITAL LETTER OMICRON WITH VARIA Ὸ
unter http://unicode.org/cldr/utility/character.jsp?a=1FF8 mit einem "uca" ( dh Level 1 Sortiergewicht / Sortierelement) von 5F30
. Wenn Sie auf "5F30" klicken, gelangen Sie zu einer Suchseite - http://unicode.org/cldr/utility/list-unicodeset.jsp?a=%5B%3Auca%3D5F30%3A%5D - mit 30 Übereinstimmungen, davon 20 Sie liegen im Bereich von 0 bis 65535 (dh U + 0000 - U + FFFF). Wenn wir in der Datei allkeys.txt nach Code Point 1FF8 suchen , sehen wir ein Sortiergewicht von Stufe 1 12E0
. Machen Sie eine "Zählung" in Notepad ++ auf12E0.
zeigt 30 Übereinstimmungen an (dies entspricht den Ergebnissen von Unicode.org, obwohl dies nicht garantiert ist, da die Datei für Unicode 5.0 ist und die Site Unicode 9.0-Daten verwendet).
In SQL Server gibt die folgende Abfrage 20 Übereinstimmungen zurück, genau wie die Unicode.org-Suche, wenn die 10 zusätzlichen Zeichen entfernt werden:
;WITH cte AS
(
SELECT TOP (65535) ROW_NUMBER() OVER (ORDER BY (SELECT 0)) AS [Num]
FROM [master].sys.columns col
CROSS JOIN [master].sys.objects obj
)
SELECT cte.Num AS [Decimal],
CONVERT(VARCHAR(50), CONVERT(VARBINARY(2), cte.Num), 2) AS [Hex],
NCHAR(cte.Num) AS [Character]
FROM cte
WHERE NCHAR(cte.Num) = NCHAR(0x1FF8) COLLATE Latin1_General_100_CI_AI
ORDER BY cte.Num;
Zur Sicherheit kehren Sie zur Seite ICU Collation Demo zurück und ersetzen Sie die Zeichen im Feld "Eingabe" durch die folgenden 3 Zeichen aus der Liste der 20 Ergebnisse von SQL Server:
Ὂ
𝜪
Ὸ
zeigt, dass sie tatsächlich alle dasselbe 5F 30
Sortiergewicht der Ebene 1 haben (passend zum "uca" -Feld auf der Charaktereigenschaftsseite).
SO, scheint es sicher , als ob dieser besondere Charakter sollte nicht etwas anderes anzeigen lassen.
Wie es tatsächlich funktioniert (zumindest in Microsoft-Land)
Im Gegensatz zu SQL Server kann in .NET der Sortierschlüssel für eine Zeichenfolge mithilfe der CompareInfo.GetSortKey-Methode angezeigt werden . Wenn Sie diese Methode verwenden und nur das U + FFFD-Zeichen übergeben, wird der Sortierschlüssel von zurückgegeben 0x0101010100
. Durchlaufen Sie dann alle Zeichen im Bereich von 0 bis 65535, um festzustellen, welche einen Sortierschlüssel mit 0x0101010100
4529 zurückgegebenen Übereinstimmungen hatten. Dies stimmt nicht genau mit dem in SQL Server zurückgegebenen 5840 überein (wenn die Latin1_General_100_CS_AS_WS
Sortierung verwendet wird), aber es ist ( vorerst) das Beste, was wir bekommen können, wenn ich Windows 10 und .NET Framework Version 4.6.1 verwende, das Unicode v verwendet 6.3.0 gemäß der Tabelle für die CharUnicodeInfo-Klasse(in "Hinweis für Anrufer" im Abschnitt "Bemerkungen"). Im Moment verwende ich eine SQLCLR-Funktion und kann daher die Ziel-Framework-Version nicht ändern. Wenn ich eine Chance bekomme, erstelle ich eine Konsolen-App und verwende eine Ziel-Framework-Version von 4.5, da diese Unicode 5.0 verwendet, das den Kollatierungen der 100er-Reihe entsprechen sollte.
Was dieser Test zeigt, ist, dass selbst ohne die exakt gleiche Anzahl von Übereinstimmungen zwischen .NET und SQL Server für U + FFFD ziemlich klar ist, dass dies kein SQL Server-spezifisches Verhalten ist und ob beabsichtigt oder mit der Implementierung beaufsichtigt von Microsoft stimmt das U + FFFD-Zeichen in der Tat mit einigen Zeichen überein, auch wenn es nicht der Unicode-Spezifikation entsprechen sollte. Und da dieses Zeichen mit U + 0000 (null) übereinstimmt, handelt es sich wahrscheinlich nur um ein Problem mit fehlenden Gewichten.
EBENFALLS
In Bezug auf den Unterschied im Verhalten der =
Abfrage gegenüber der LIKE N'%�%'
Abfrage hat dies mit den Platzhaltern und den fehlenden (ich nehme an) Gewichten für diese (dh � Ƕ Ƿ Ǹ
) Zeichen zu tun . Wenn die LIKE
Bedingung in "einfach" geändert wird, werden LIKE N'�'
die gleichen 3 Zeilen wie die =
Bedingung zurückgegeben. Wenn das Problem mit den Platzhaltern nicht auf "fehlende" Gewichte zurückzuführen ist (es wird übrigens kein 0x00
Sortierschlüssel zurückgegeben CompareInfo.GetSortKey
), kann dies daran liegen, dass diese Zeichen möglicherweise eine Eigenschaft haben, mit der der Sortierschlüssel kontextabhängig variiert (dh umgebende Zeichen) ).
FFFD
(Suche nach*0F12.0020.0002.FFFD
nur einem Ergebnis). Aus der Beobachtung von @ Forrest, dass sie alle mit der leeren Zeichenfolge übereinstimmen und etwas mehr über das Thema lesen, geht hervor, dass das Gewicht, das sie in den verschiedenen nicht-binären Kollatierungen teilen, tatsächlich Null ist, glaube ich.