Porównanie jednej „postaci” (która może składać się z wielu Punktów Kodowych: par zastępczych, łączenia znaków itp.) Z inną, opiera się na dość złożonym zestawie reguł. Jest to tak skomplikowane, że wymaga uwzględnienia wszystkich różnych (a czasem „zwariowanych”) reguł znalezionych we wszystkich językach reprezentowanych w specyfikacji Unicode . Ten system ma zastosowanie do niebinarnych zestawień dla wszystkich NVARCHAR
danych oraz dla VARCHAR
danych korzystających z sortowania Windows, a nie z SQL Server Collation (zaczynającego się od SQL_
). Ten system nie ma zastosowania do VARCHAR
danych korzystających z sortowania SQL Server, ponieważ używają one prostych mapowań.
Większość reguł jest zdefiniowanych w algorytmie sortowania Unicode (UCA) . Niektóre z tych zasad, a niektóre nieobjęte UCA, to:
- Domyślna kolejność / waga podane w
allkeys.txt
pliku (odnotowane poniżej)
- Jakie wrażliwości i opcje są używane (np. Czy rozróżnia małe i duże litery? A jeśli jest wrażliwe, to czy najpierw jest pisana wielkimi lub małymi literami?)
- Wszelkie przesłonięcia oparte na ustawieniach regionalnych.
- Używana jest wersja standardu Unicode.
- Czynnik „ludzki” (tj. Unicode jest specyfikacją, a nie oprogramowaniem, dlatego też każdy dostawca musi ją wdrożyć)
Podkreśliłem, że ostatnia kwestia dotycząca czynnika ludzkiego powinna wyjaśnić, że nie należy oczekiwać, że SQL Server zawsze zachowa się w 100% zgodnie ze specyfikacją.
Czynnikiem nadrzędnym w tym przypadku jest waga przypisana do każdego punktu kodowego oraz fakt, że wiele punktów kodowych może mieć tę samą specyfikację wagi. Podstawowe wagi (bez zastąpień specyficznych dla ustawień narodowych) można znaleźć tutaj (uważam, że 100
serią zestawień jest Unicode v 5.0 - nieformalne potwierdzenie w komentarzach do elementu Microsoft Connect ):
http://www.unicode.org/Public/UCA/5.0.0/allkeys.txt
Punkt kodowy, o którym mowa - U + FFFD - jest zdefiniowany jako:
FFFD ; [*0F12.0020.0002.FFFD] # REPLACEMENT CHARACTER
Zapis ten jest zdefiniowany w sekcji 9.1 Format pliku wszystkich klawiszy UCA:
<entry> := <charList> ';' <collElement>+ <eol>
<charList> := <char>+
<collElement> := "[" <alt> <weight> "." <weight> "." <weight> ("." <weight>)? "]"
<alt> := "*" | "."
Collation elements marked with a "*" are variable.
Ta ostatnia linia jest ważna, ponieważ punkt kodowy, na który patrzymy, ma specyfikację zaczynającą się od „*”. W sekcji 3.6 Ważenie zmiennych zdefiniowano cztery możliwe zachowania, oparte na wartościach konfiguracji sortowania, do których nie mamy bezpośredniego dostępu (są one zakodowane na stałe w implementacji Microsoft dla każdego sortowania, np. Czy rozróżniana jest wielkość liter w pierwszej kolejności czy po pierwsze, właściwość, która różni się między VARCHAR
danymi za pomocą SQL_
sortowania i wszystkimi innymi odmianami).
Nie mam czasu na pełne zbadanie, które ścieżki są brane, i na ustalenie, które opcje są stosowane, aby można było podać bardziej solidny dowód, ale można bezpiecznie powiedzieć, że w ramach każdej specyfikacji Code Point, czy coś uważane za „równe” nie zawsze będzie korzystało z pełnej specyfikacji. W tym przypadku mamy „0F12.0020.0002.FFFD” i najprawdopodobniej wykorzystywane są tylko poziomy 2 i 3 (tj . .0020.0002. ). Wykonanie „Count” w Notepad ++ dla „.0020.0002”. znajduje 12 581 dopasowań (w tym dodatkowe postacie, z którymi jeszcze nie mieliśmy do czynienia). Wykonanie „Count” na „[*” zwraca 4049 dopasowań. Wykonywanie RegEx „Find” / „Count” przy użyciu wzorca\[\*\d{4}\.0020\.0002
zwraca 832 dopasowania. Więc gdzieś w tej kombinacji, a także niektóre inne reguły, których nie widzę, a także niektóre szczegóły dotyczące implementacji specyficzne dla Microsoft, to pełne wyjaśnienie tego zachowania. I dla jasności, zachowanie jest takie samo dla wszystkich pasujących znaków, ponieważ wszystkie one pasują do siebie, ponieważ wszystkie mają taką samą wagę po zastosowaniu reguł (co oznacza, że to pytanie można było zadać na temat każdego z nich, a nie koniecznie Mr. �
).
Możesz zobaczyć z zapytaniem poniżej i zmienić COLLATE
klauzulę zgodnie z wynikami pod zapytaniem, jak działają różne wrażliwości w dwóch wersjach Collation:
;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;
Różne liczby pasujących znaków w różnych układach znajdują się poniżej.
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
We wszystkich wymienionych powyżej zestawieniach N'' = N'�'
również ma wartość true.
AKTUALIZACJA
Byłem w stanie przeprowadzić trochę więcej badań i oto, co znalazłem:
Jak to „prawdopodobnie” powinno działać
Korzystając z ICU Collation Demo , ustawiłem ustawienia regionalne na „en-US-u-va-posix”, ustawiłem siłę na „podstawową”, zaznaczone pokaż „klucze sortowania” i wkleiłem 4 następujące znaki, które skopiowałem z wyniki powyższego zapytania (przy użyciu Latin1_General_100_CI_AI
sortowania):
�
Ԩ
ԩ
Ԫ
i to zwraca:
Ԫ
60 2E 02 .
Ԩ
60 7A .
ԩ
60 7A .
�
FF FD .
Następnie sprawdź właściwości znaku dla „ ” na stronie http://unicode.org/cldr/utility/character.jsp?a=fffd i sprawdź, czy klucz sortowania na poziomie 1 (tj. FF FD
) Odpowiada właściwości „uca”. Kliknięcie tej właściwości „uca” prowadzi do strony wyszukiwania - http://unicode.org/cldr/utility/list-unicodeset.jsp?a=%5B%3Auca%3DFFFD%3A%5D - pokazując tylko 1 dopasowanie. I w pliku allkeys.txt waga sortowania poziomu 1 jest pokazana jako 0F12
i istnieje tylko 1 dopasowanie do tego.
Aby upewnić się, że interpretujemy to zachowanie poprawnie, spojrzałem na inny znak: GRECKI LITER OMICRON Z VARIĄ Ὸ
na stronie http://unicode.org/cldr/utility/character.jsp?a=1FF8, który ma „uca” ( tzn. sortowanie / sortowanie na poziomie 1) 5F30
. Kliknięcie tego „5F30” prowadzi nas do strony wyszukiwania - http://unicode.org/cldr/utility/list-unicodeset.jsp?a=%5B%3Auca%3D5F30%3A%5D - pokazuje 30 dopasowań, 20 z mieszczą się w zakresie 0–65535 (tj. U + 0000 - U + FFFF). Patrząc w allkeys.txt plik dla Code Point 1FF8 widzimy poziom 1 Ciężar sortowania 12E0
. Wykonanie „Count” w Notepad ++ na12E0.
pokazuje 30 dopasowań (odpowiada to wynikom z Unicode.org, choć nie jest to gwarantowane, ponieważ plik dotyczy Unicode v 5.0, a strona korzysta z danych Unicode v 9.0).
W SQL Server następujące zapytanie zwraca 20 dopasowań, takich samych jak wyszukiwanie Unicode.org podczas usuwania 10 znaków uzupełniających:
;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;
I, dla pewności, wracając do strony ICU Collation Demo i zastępując znaki w polu „Input” następującymi 3 znakami pobranymi z listy 20 wyników z SQL Server:
Ὂ
𝜪
Ὸ
pokazuje, że rzeczywiście wszystkie mają taką samą 5F 30
wagę sortowania poziomu 1 (pasującą do pola „uca” na stronie właściwości postaci).
SO, z pewnością wydaje się, że ta konkretna postać nie powinna pasować do niczego innego.
Jak to faktycznie działa (przynajmniej w Microsoft-land)
W przeciwieństwie do SQL Server, .NET umożliwia wyświetlanie klucza sortowania dla ciągu za pomocą metody CompareInfo.GetSortKey . Używając tej metody i przekazując tylko znak U + FFFD, zwraca klucz sortowania 0x0101010100
. Następnie, iterując wszystkie znaki z zakresu od 0 do 65535, aby zobaczyć, które z nich mają klucz sortowania 0x0101010100
zwróconych 4529 dopasowań. To nie jest dokładnie zgodne z 5840 zwróconym w SQL Server (podczas korzystania z Latin1_General_100_CS_AS_WS
sortowania), ale jest to najbliższe, jakie możemy uzyskać (na razie), biorąc pod uwagę, że korzystam z systemu Windows 10 i .NET Framework w wersji 4.6.1, który używa Unicode v 6.3.0 zgodnie z wykresem dla klasy CharUnicodeInfo(w „Uwaga dla dzwoniących”, w sekcji „Uwagi”). W tej chwili używam funkcji SQLCLR, więc nie mogę zmienić docelowej wersji Framework. Kiedy będę miał okazję, stworzę aplikację konsolową i użyję docelowej wersji Framework 4.5, która używa Unicode v 5.0, który powinien pasować do Collations serii 100.
Ten test pokazuje, że nawet bez dokładnie takiej samej liczby dopasowań między .NET a SQL Server dla U + FFFD, jest całkiem jasne, że nie jest to zachowanie specyficzne dla SQL Server, i czy celowe lub nadzór z wykonaną implementacją przez Microsoft znak U + FFFD rzeczywiście pasuje do kilku znaków, nawet jeśli nie powinien być zgodny ze specyfikacją Unicode. A biorąc pod uwagę, że ta postać pasuje do U + 0000 (zero), prawdopodobnie jest to tylko kwestia braku wag.
RÓWNIEŻ
Jeśli chodzi o różnicę w zachowaniu w =
zapytaniu w porównaniu z LIKE N'%�%'
zapytaniem, ma to związek z symbolami wieloznacznymi i brakującymi (zakładam) wagami dla tych (tj. � Ƕ Ƿ Ǹ
) Znaków. Jeśli LIKE
warunek zostanie zmieniony na zwykły, LIKE N'�'
wówczas zwróci te same 3 wiersze co =
warunek. Jeśli problem ze znakami wieloznacznymi nie wynika z „brakujących” wag (nie ma 0x00
zwracanego klucza sortowania przez CompareInfo.GetSortKey
, btw), może to być spowodowane tym, że te znaki mogą mieć właściwość, która pozwala na zmianę klucza sortowania w zależności od kontekstu (tj. Znaki otaczające ).
FFFD
(wyszukiwanie*0F12.0020.0002.FFFD
tylko jednego wyniku zwraca). Z obserwacji @ Forrest, że wszystkie pasują do pustego łańcucha i trochę więcej lektury na ten temat, wygląda na to, że waga, jaką mają one w różnych niebinarnych zestawieniach, jest w rzeczywistości zerowa.