Accent Sensitive Sort

19

Dlaczego te dwie SELECTinstrukcje powodują inną kolejność sortowania?

USE tempdb;
CREATE TABLE dbo.OddSort 
(
    id INT IDENTITY(1,1) PRIMARY KEY
    , col1 NVARCHAR(2)
    , col2 NVARCHAR(2)
);
GO
INSERT dbo.OddSort (col1, col2) 
VALUES (N'e', N'eA')
    , (N'é', N'éB')
    , (N'ë', N'ëC')
    , (N'è', N'èD')
    , (N'ê', N'êE')
    , (N'ē', N'ēF');
GO

SELECT * 
FROM dbo.OddSort 
ORDER BY col1 COLLATE Latin1_General_100_CS_AS;
╔════╦══════╦══════╗
║ id ║ col1 ║ col2 ║
╠════╬══════╬══════╣
║ 1 ║ e ║ eA ║
║ 2 ║ é ║ éB ║
║ 4 ║ è ║ èD ║ - powinno być id 3?
║ 5 ║ ê ║ êE ║
║ 3 ║ ë ║ ëC ║
║ 6 ║ ē ║ ēF ║
╚════╩══════╩══════╝
SELECT * 
FROM dbo.OddSort 
ORDER BY col2 COLLATE Latin1_General_100_CS_AS;
╔════╦══════╦══════╗
║ id ║ col1 ║ col2 ║
╠════╬══════╬══════╣
║ 1 ║ e ║ eA ║
║ 2 ║ é ║ éB ║
║ 3 ║ ë ║ ëC ║
║ 4 ║ è ║ èD ║
║ 5 ║ ê ║ êE ║
║ 6 ║ ē ║ ēF ║
╚════╩══════╩══════╝
Aram
źródło

Odpowiedzi:

13

To pytanie nie jest tak związane z bazami danych, ale raczej z obsługą i regułami Unicode.

Na podstawie https://docs.microsoft.com/en-us/sql/t-sql/statements/windows-collation-name-transact-sql Latin1_General_100_CS_AS oznacza: „Sortowanie używa reguł sortowania i mapowania słownika Latin1 General na stronę kodową 1252 ”z dodanym CS = rozróżnianie wielkości liter i AS = rozróżnianie wielkości liter.

Odwzorowanie między stroną kodową Windows 1252 a Unicode ( http://www.unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/CP1252.TXT ) pokazuje te same wartości dla wszystkich znaków, z którymi mamy do czynienia (z wyjątkiem e z makronem tego nie ma w mapowaniu Microsoft, więc nie mam pojęcia, co robi z tym przypadkiem), więc na razie możemy skoncentrować się na narzędziach i terminologii Unicode.

Po pierwsze, daj nam znać dokładnie, z czym mamy do czynienia, dla wszystkich swoich ciągów:

0065  LATIN SMALL LETTER E
0041  LATIN CAPITAL LETTER A
00E9  LATIN SMALL LETTER E WITH ACUTE
0042  LATIN CAPITAL LETTER B
00EB  LATIN SMALL LETTER E WITH DIAERESIS
0043  LATIN CAPITAL LETTER C
00E8  LATIN SMALL LETTER E WITH GRAVE
0044  LATIN CAPITAL LETTER D
00EA  LATIN SMALL LETTER E WITH CIRCUMFLEX
0045  LATIN CAPITAL LETTER E
0113  LATIN SMALL LETTER E WITH MACRON
0046  LATIN CAPITAL LETTER F

Algorytm sortowania Unicode opisano tutaj: https://www.unicode.org/reports/tr10/

Spójrz na sekcję 1.3 „Wrażliwość kontekstowa”, która wyjaśnia, że ​​sortowanie nie może zależeć tylko od jednego znaku po drugim, ponieważ niektóre reguły są zależne od kontekstu.

Zwróć także uwagę na te punkty w 1.8:

Sortowanie nie jest własnością ciągów. Generalnie porządek sortowania nie jest zachowywany podczas operacji konkatenacji lub podciągania.

Domyślnie algorytm wykorzystuje trzy w pełni konfigurowalne poziomy. W przypadku skryptu łacińskiego poziomy te odpowiadają mniej więcej:

alphabetic ordering
diacritic ordering
case ordering.

Ale sam algorytm jest trochę gęsty. Jego istotą jest: Krótko mówiąc, algorytm sortowania Unicode pobiera wejściowy ciąg Unicode i tablicę elementów sortowania, zawierającą dane odwzorowania znaków. Tworzy klucz sortowania, który jest tablicą 16-bitowych liczb całkowitych bez znaku. Tak wytworzone dwa lub więcej kluczy sortowania można następnie porównać binarnie, aby uzyskać prawidłowe porównanie ciągów, dla których zostały wygenerowane.

Możesz zobaczyć szczegółowe zasady sortowania w języku łacińskim tutaj: http://developer.mimer.com/collations/charts/latin.htm lub więcej bezpośrednio i specjalnie dla MS SQL: http://collation-charts.org/mssql/mssql. 0409.1252.Latin1_General_CS_AS.html

Dla epostaci pokazuje:

e E é É è È ê Ê ë Ë

Wyjaśnia to twoje wyniki przy zamawianiu col1z tą różnicą, że ē nie istnieje na stronie kodowej 1252, więc absolutnie nie mam pojęcia, co on z tym robi.

Lub jeśli wykonujemy algorytm Unicode ręcznie, używając wartości kluczy DUCET na stronie http://www.unicode.org/Public/UCA/latest/allkeys.txt :

krok 1: Normalizacja z formularza D, więc każdy przypadek staje się:

e => U+0065
é => U+0065 U+0301
ë => U+0065 U+0308
è => U+0065 U+0300
ê => U+0065 U+0302
ē => U+0065 U+0304

krok 2, Utwórz tablice sortowania (wyszukiwanie w pliku allkeys.txt)

e => [.1D10.0020.0002]
é => [.1D10.0020.0002] [.0000.0024.0002]
ë => [.1D10.0020.0002] [.0000.002B.0002]
è => [.1D10.0020.0002] [.0000.0025.0002]
ê => [.1D10.0020.0002] [.0000.0027.0002]
ē => [.1D10.0020.0002] [.0000.0032.0002]

krok 3, Utwórz klucze sortowania (dla każdego poziomu, weź każdą wartość do każdej tablicy zestawiania, następnie umieść 0000 jako separator i zacznij ponownie na następny poziom)

e => 1D10 0000 0020 0000 0002
é => 1D10 0000 0020 0024 0000 0002 0002
ë => 1D10 0000 0020 002B 0000 0002 0002
è => 1D10 0000 0020 0025 0000 0002 0002
ê => 1D10 0000 0020 0027 0000 0002 0002
ē => 1D10 0000 0020 0032 0000 0002 0002

krok 4, Porównaj klucze sortowania (proste binarne porównanie każdej wartości jedna po drugiej): Czwarta wartość wystarczy, aby posortować je wszystkie, więc ostateczna kolejność to:

e
é
è
ê
ë
ē

W ten sam sposób przy zamawianiu col2:

krok 1: NFD

eA => U+0065 U+0041
éB => U+0065 U+0301 U+0042
ëC => U+0065 U+0308 U+0043
èD => U+0065 U+0300 U+0044
êE => U+0065 U+0302 U+0045
ēF => U+0065 U+0304 U+0046

krok 2: Tablice zestawiania

eA => [.1D10.0020.0002] [.1CAD.0020.0008]
éB => [.1D10.0020.0002] [.0000.0024.0002] [.1CC6.0020.0008]
ëC => [.1D10.0020.0002] [.0000.002B.0002] [.1CE0.0020.0008]
èD => [.1D10.0020.0002] [.0000.0025.0002] [.1CF5.0020.0008]
êE => [.1D10.0020.0002] [.0000.0027.0002] [.1D10.0020.0008]
ēF => [.1D10.0020.0002] [.0000.0032.0002] [.1D4B.0020.0008]

krok 3: Utwórz klucze sortowania

eA => 1D10 1CAD 0000 0020 0020 0000 0002 0008
éB => 1D10 1CC6 0000 0020 0024 0020 0000 0002 0002 0008
ëC => 1D10 1CE0 0000 0020 002B 0020 0000 0002 0002 0008
èD => 1D10 1CF5 0000 0020 0025 0020 0000 0002 0002 0008
êE => 1D10 1D10 0000 0020 0027 0020 0000 0002 0002 0008
ēF => 1D10 1D4B 0000 0020 0032 0020 0000 0002 0002 0008

krok 4: Porównaj klucze sortowania: Druga wartość wystarczy, aby posortować je wszystkie, i w rzeczywistości jest już w porządku rosnącym, więc ostateczna kolejność to:

eA
éB
ëC
èD
êE
ēF

Aktualizacja : dodanie trzeciego przypadku Solomona Rutzky'ego, który jest trudniejszy ze względu na przestrzeń umożliwiającą wprowadzenie nowych reguł (wybrałem „przypadek niezapominalny”):

krok 1, NFD:

è 1 => U+0065 U+0300 U+0020 U+0031
ê 5 => U+0065 U+0302 U+0020 U+0035
e 2 => U+0065 U+0020 U+0032
é 4 => U+0065 U+0301 U+0020 U+0034
ē 3 => U+0065 U+0304 U+0020 U+0033
ë 6 => U+0065 U+0308 U+0020 U+0036

krok 2, Utwórz tablice zestawień:

è 1 => [.1D10.0020.0002] [.0000.0025.0002] [*0209.0020.0002] [.1CA4.0020.0002]
ê 5 => [.1D10.0020.0002] [.0000.0027.0002] [*0209.0020.0002] [.1CA8.0020.0002]
e 2 => [.1D10.0020.0002] [*0209.0020.0002] [.1CA5.0020.0002]
é 4 => [.1D10.0020.0002] [.0000.0024.0002] [*0209.0020.0002] [.1CA7.0020.0002]
ē 3 => [.1D10.0020.0002] [.0000.0032.0002] [*0209.0020.0002] [.1CA6.0020.0002]
ë 6 => [.1D10.0020.0002] [.0000.002B.0002] [*0209.0020.0002] [.1CA9.0020.0002]

krok 3, Utwórz klucze sortowania:

è 1 => 1D10 0209 1CA4 0000 0020 0025 0020 0020 0000 0002 0002 0002 0002
ê 5 => 1D10 0209 1CA8 0000 0020 0027 0020 0020 0000 0002 0002 0002 0002
e 2 => 1D10 0209 1CA5 0000 0020 0020 0020 0000 0002 0002 0002
é 4 => 1D10 0209 1CA7 0000 0020 0024 0020 0020 0000 0002 0002 0002 0002
ē 3 => 1D10 0209 1CA6 0000 0020 0032 0020 0020 0000 0002 0002 0002 0002
ë 6 => 1D10 0209 1CA9 0000 0020 002B 0020 0020 0000 0002 0002 0002 0002

krok 4, Porównaj klucze sortowania:

Zasadniczo trzecia wartość określa kolejność i w rzeczywistości opiera się tylko na ostatniej cyfrze, więc kolejność powinna wynosić:

è 1
e 2
ē 3
é 4
ê 5
ë 6

Druga aktualizacja oparta na komentarzu Solomona Rutzky'ego na temat wersji Unicode.

Użyłem allkeys.txtdanych o najnowszej wersji Unicode, czyli wersji 10.0

Jeśli zamiast tego musimy wziąć pod uwagę Unicode 5.1 , byłoby to: http://www.unicode.org/Public/UCA/5.1.0/allkeys.txt

Właśnie sprawdziłem, dla wszystkich powyższych znaków, tablice sortowania są następujące:

e => [.119D.0020.0002.0065]
é => [.119D.0020.0002.0065] [.0000.0032.0002.0301]
ë => [.119D.0020.0002.0065] [.0000.0047.0002.0308]
è => [.119D.0020.0002.0065] [.0000.0035.0002.0300]
ê => [.119D.0020.0002.0065] [.0000.003C.0002.0302]
ē => [.119D.0020.0002.0065] [.0000.005B.0002.0304]

i:

eA => [.119D.0020.0002.0065] [.1141.0020.0008.0041]
éB => [.119D.0020.0002.0065] [.0000.0032.0002.0301] [.1157.0020.0008.0042]
ëC => [.119D.0020.0002.0065] [.0000.0047.0002.0308] [.116F.0020.0008.0043]
èD => [.119D.0020.0002.0065] [.0000.0035.0002.0300] [.1182.0020.0008.0044]
êE => [.119D.0020.0002.0065] [.0000.003C.0002.0302] [.119D.0020.0008.0045]
ēF => [.119D.0020.0002.0065] [.0000.005B.0002.0304] [.11D5.0020.0008.0046]

i:

è 1 => [.119D.0020.0002.0065] [.0000.0035.0002.0300] [*0209.0020.0002.0020] [.1138.0020.0002.0031]
ê 5 => [.119D.0020.0002.0065] [.0000.003C.0002.0302] [*0209.0020.0002.0020] [.113C.0020.0002.0035]
e 2 => [.119D.0020.0002.0065] [*0209.0020.0002.0020] [.1139.0020.0002.0032]
é 4 => [.119D.0020.0002.0065] [.0000.0032.0002.0301] [*0209.0020.0002.0020] [.113B.0020.0002.0034]
ē 3 => [.119D.0020.0002.0065] [.0000.005B.0002.0304] [*0209.0020.0002.0020] [.113A.0020.0002.0033]
ë 6 => [.119D.0020.0002.0065] [.0000.0047.0002.0308] [*0209.0020.0002.0020] [.113D.0020.0002.0036]

które następnie obliczają następujące klucze sortowania:

e => 119D 0000 0020 0000 0002 0000 0065
é => 119D 0000 0020 0032 0000 0002 0002 0000 0065 0301
ë => 119D 0000 0020 0047 0000 0002 0002 0000 0065 0308
è => 119D 0000 0020 0035 0000 0002 0002 0000 0065 0300
ê => 119D 0000 0020 003C 0000 0002 0002 0000 0065 0302
ē => 119D 0000 0020 005B 0000 0002 0002 0000 0065 0304

i:

eA => 119D 1141 0000 0020 0020 0000 0002 0008 0000 0065 0041
éB => 119D 1157 0000 0020 0032 0020 0000 0002 0002 0008 0000 0065 0301 0042
ëC => 119D 116F 0000 0020 0047 0020 0000 0002 0002 0008 0000 0065 0308 0043
èD => 119D 1182 0000 0020 0035 0020 0000 0002 0002 0008 0000 0065 0300 0044
êE => 119D 119D 0000 0020 003C 0020 0000 0002 0002 0008 0000 0065 0302 0045
ēF => 119D 11D5 0000 0020 005B 0020 0000 0002 0002 0008 0000 0065 0304 0046

i:

è 1 => 119D 0209 1138 0000 0020 0035 0020 0020 0000 0002 0002 0002 0002 0000 0065 0300 0020 0031
ê 5 => 119D 0209 113C 0000 0020 003C 0020 0020 0000 0002 0002 0002 0002 0000 0065 0302 0020 0035
e 2 => 119D 0209 1139 0000 0020 0020 0020 0000 0002 0002 0002 0000 0065 0020 0032
é 4 => 119D 0209 113B 0000 0020 0032 0020 0020 0000 0002 0002 0002 0002 0000 0065 0301 0020 0034
ē 3 => 119D 0209 113A 0000 0020 005B 0020 0020 0000 0002 0002 0002 0002 0000 0065 0304 0020 0033
ë 6 => 119D 0209 113D 0000 0020 0047 0020 0020 0000 0002 0002 0002 0002 0000 0065 0308 0020 0036

co ponownie daje te trzy posortowane wyniki:

e
é
è
ê
ë
ē

i

eA
éB
ëC
èD
êE
ēF

i

è 1
e 2
ē 3
é 4
ê 5
ë 6
Patrick Mevzek
źródło
Cześć Patryk. Dziękujemy za opublikowanie tych szczegółowych informacji. Kilka uwag: 1) Możesz zignorować kod strony 1252. Dotyczy to danych VARCHAR(tj. Nie-Unicode), które nie są tutaj używane. Właśnie dlatego ēpostać działa dobrze. 2) Informacje o „zestawieniach” są nieco nieaktualne. Dotyczy poprzedniej wersji tego zestawu i nie opublikowali niczego od 2009 roku. 3) Wersja Unicode tutaj zdecydowanie nie jest najnowsza (wersja 10). _100_Seria Konfrontacje przyszedł z SQL 2008, więc będzie to Unicode 5.0 lub 5.1: unicode.org/standard/versions/#TUS_Earlier_Versions
Salomon Rutzky
Nie sądzę, aby allKeys.txt zmiany między wersją Unicode, oprócz dodania nowych znaków, więc powyższe powinno pozostać prawdziwe, ale oczywiście może być powtórzone z poprzednimi starymi danymi, po prostu brakuje mi energii włożenia w to kilku godzin. Jeśli chodzi o CP1252, to po prostu pochodzi z definicji podanej przez MS-SQL (sam nie używam tego produktu).
Patrick Mevzek,
1) Prawdopodobnie nie ma większych zmian między wersjami, ale jestem całkiem pewien, że istnieją przynajmniej poprawki dotyczące wagi / klasyfikacji. Ale tak, z pewnością dostaję ograniczenia czasowe;) 2) Jeśli chodzi o CP1252, wspominałem o tym, ponieważ koncepcja stron kodowych nie istnieje w Unicode. Unicode to sposób na brak potrzeby stron kodowych. Dokument MS jest wprawdzie niejasny, ale wspomina w górnej części „ Strona kodowa używana do przechowywania danych znaków nieobsługujących kodu Unicode ”. Masz rację, że jednej postaci nie ma w CP1252, ale strony kodowe nie wchodzą tutaj w grę.
Solomon Rutzky
Tak, nigdy nie mówiłem nic o stronie kodowej związanej z Unicode. Tylko dokumentacja MS SQL mówi, że ta nazwa sortowania działa z „stroną kodową 1252”. Prawdopodobnie nie ma to sensu (nie dla mnie, ale znowu, nie jest to jego użytkownik), ale tak napisano w dokumentacji tego oprogramowania. Jeśli chodzi o ponowne wykonanie zadania, możesz to zrobić lub po prostu podaj zmiany dotyczące sortowania związane z postaciami w grze tutaj między Unicode 5 i najnowszymi, jeśli są i jeśli chcesz. Uważam, że moja odpowiedź jest taka, jaka jest, jest precyzyjna i opiera wyniki na podstawie danych wejściowych, a nie odwrotnie.
Patrick Mevzek,
3) re: zdanie nr 1: chociaż głównie dotyczy Unicode, to pytanie jest problemem systemu operacyjnego, ponieważ MS wdrożyło specyfikację i mogło nie zrobić wszystkiego i / lub mogło popełnić kilka błędów. Znalazłem 2 błędy w .NET dotyczące sposobu, w jaki określa „kategorię” lub „blok”, w którym znajduje się dana postać (błędnie identyfikują mały segment znaków). Również to jest , przynajmniej nieznacznie, to kwestia SQL Server, tylko dlatego, że SQL Server skutecznie migawkę każdego Układanie w danym momencie (dla spójności między wersjami), aby mogli mieć to, co jest teraz SQL Server specyficzne zachowanie.
Solomon Rutzky
16

Zachowanie, które tutaj widzisz, wynika ogólnie z faktu, że algorytm sortowania Unicode (UCA) pozwala na złożone, wielopoziomowe sortowanie. Dokładniej:

  1. Sortowanie to nie porównanie:

    Ustalenie, czy dwa ciągi znaków są takie same czy różne, jest dość proste (biorąc pod uwagę określone ustawienia regionalne / język i zestaw wrażliwości). Ale określenie kolejności 2 lub więcej ciągów może być bardzo złożone.

  2. Sortowanie odbywa się w szeregu kroków, przy czym każdy krok jest stosowany do całego łańcucha, a nie znak po znaku:

    1. Standard: sortuj znaki podstawowe (niezależnie od różnic akcentu i wielkości liter)
    2. JEŚLI Wrażliwy na akcent, zastosuj odważniki akcentujące / diakrytyczne
    3. JEŚLI rozróżniana jest wielkość liter, zastosuj ciężary osłon

Kiedy sortujesz według col1(pojedynczego znaku), najpierw określa, że ​​wszystkie znaki mają taką samą wagę, ponieważ wszystkie są „ e ”. Następnie stosuje wagi akcentujące / diakrytyczne. Nie ma różnic w obudowie, więc trzeci krok nic nie zmieni. Tak więc jedyne różnice występują w kroku 2 i dlatego istnieje preferowana kolejność dla tych wierszy na podstawie col1.

Kiedy sortujesz według col2 (dwóch znaków), najpierw określa, że ​​każdy wiersz ma inną wagę, ponieważ oba znaki są używane do określenia wagi sortowania (np. „ Ea ”, „ eb ” itp.). Następnie stosuje wagi akcentujące / diakrytyczne. Nie ma różnic w obudowie, więc trzeci krok nic nie zmieni. Tym razem istnieją różnice w krokach 1 i 2. Ale ponieważ różnice w kroku 1 zostały już zastosowane do każdego łańcucha przed uwzględnieniem wag z kroku 2, wagi z kroku 2 nie mają żadnego wpływu na kolejność; będą miały zastosowanie tylko wtedy, gdy wagi z kroku 1 dla dwóch lub więcej rzędów będą takie same.

Następująca adaptacja przykładowego kodu z pytania, miejmy nadzieję, ilustruje opisane powyżej zachowanie związane z sortowaniem. Dodałem kilka dodatkowych wierszy i dodatkową kolumnę, aby pokazać wpływ rozróżniania wielkości liter w sortowaniu (ponieważ w oryginalnych przykładowych danych wszystkie małe litery):

USTAWIAĆ

USE [tempdb];

-- DROP TABLE dbo.OddSort;
CREATE TABLE dbo.OddSort
(
    id INT IDENTITY(1,1) PRIMARY KEY,
    col1 NVARCHAR(5) COLLATE Latin1_General_100_CS_AS,
    col2 NVARCHAR(5) COLLATE Latin1_General_100_CS_AS,
    col3 NVARCHAR(5) COLLATE Latin1_General_100_CS_AS
);
GO

INSERT dbo.OddSort (col1, col2, col3)
VALUES (N'e', N'eA', N'e A')
     , (N'ê', N'êE', N'ê E')
     , (N'é', N'éH', N'é H')
     , (N'ë', N'ëC', N'ë C')
     , (N'E', N'EG', N'E G')
     , (N'Ë', N'ëh', N'ë h')
     , (N'è', N'èD', N'è D')
     , (N'é', N'éB', N'é B')
     , (N'ë', N'ëH', N'ë H')
     , (N'ē', N'ēF', N'ē F');

TEST 1

SELECT [id], [col1], UNICODE([col1]) AS [CodePoint]
FROM dbo.OddSort 
ORDER BY col1;

Zwroty:

id    col1    CodePoint
1     e       101
5     E       69
8     é       233
3     é       233
7     è       232
2     ê       234
4     ë       235
9     ë       235
6     Ë       203
10    ē       275

Co możemy zobaczyć w powyższych wynikach:

  1. Punkt kodowy nie określa kolejności sortowania
  2. Nieakcentowane znaki są sortowane przed znakami akcentowanymi (w ramach tej samej litery: f po nich wszystkie pojawiałyby się ). Oczywiście, wagi akcentujące są stosowane przed ważeniem wielkości liter.
  3. Małe litery sortują przed dużymi literami w ramach tego samego akcentowanego (lub nieakcentowanego) znaku (tj. E wtedy E , a ë wtedy Ë ). To dostosowanie jest używane przez większość kolacji Windows, podczas gdy większość kolacji SQL Server najpierw sortuje wielkie litery.

TEST 2

SELECT [id], [col2]
FROM dbo.OddSort 
ORDER BY col2;

Zwroty:

id    col2
1     eA
8     éB
4     ëC
7     èD
2     êE
10    ēF
5     EG
3     éH
6     ëh
9     ëH

Co możemy zobaczyć w powyższych wynikach:

  1. Sortowanie pierwszego poziomu jest naprawdę podstawowymi postaciami. Gdyby były to akcenty / znaki diakrytyczne, wówczas wiersze ëC (id = 4), ēF (id = 10) i EG (id = 5) nie byłyby tam, gdzie są. Gdyby to była obudowa, to wiersz EG (id = 5) nie byłby na swoim miejscu.
  2. Sortowanie drugiego poziomu to naprawdę akcenty / znaki diakrytyczne. To wyjaśnia, dlaczego ostatnie trzy rzędy to éH -> ëh -> ëH zamiast ëh -> éH -> ëH (tzn. ID 3 -> 6 -> 9 zamiast 6 -> 3 -> 9).
  3. Sortowanie trzeciego poziomu to naprawdę obudowa. Właśnie dlatego ostatnie 2 wiersze to „ h -> , ponieważ najpierw sortuje się małe litery.

TEST 3

SELECT [id], [col3]
FROM dbo.OddSort 
ORDER BY col3;

Zwroty:

id    col3
1     e A
8     é B
4     ë C
7     è D
2     ê E
10    ē F
5     E G
3     é H
6     ë h
9     ë H

Co możemy zobaczyć w powyższych wynikach:

  1. Kolejność sortowania jest dokładnie taka sama jak w teście 2. Jedyna różnica w wartościach testowych polega na tym, że pomiędzy znakami jest spacja, co eliminuje możliwość istnienia reguł kontekstowych. W związku z tym wiemy, że przyczyną różnicy kolejności sortowania col2w pytaniu jest ponownie „Porównanie wielopoziomowe”, a nie „Czułość kontekstowa”.

Dodatkowe uwagi:

  1. Jeśli chodzi o uzyskanie dokładnych zasad, nie jest to tak łatwe, jak powinno być. Problem z uzyskaniem konkretnych wyjaśnień tych reguł polega na tym, że reguły sortowania Unicode, choć zdecydowanie udokumentowane, są zaleceniem. Wdrożenie tych zaleceń należy do dostawców, takich jak Microsoft. Firma Microsoft nie wdrożyła zaleceń dokładnie tak, jak podano w dokumentacji Unicode, dlatego istnieje rozłączenie (podobnie jak w przypadku, gdy ani specyfikacje HTML, ani CSS nie są wdrażane w całości, ani nawet w ten sam sposób, między dostawcami). Następnie istnieją różne wersje Windows Collations (używasz wersji, 100która wyszła z SQL Server 2008) i która jest powiązana z wersją Unicode, która jest znacznie starsza niż aktualna wersja Unicode lub demo ICU Collationjest używane. Na przykład sekcja Co nowego w SQL Server 2008 Collations w dokumentacji „Collation and Unicode Support” dla SQL Server 2008 zawiera 2 bardzo interesujące punkty na temat tego, co „nowego” w _100_serii Collations:

    1. Tabela przypadków Unicode 5.0.

      Unicode 5.0 został opublikowany w lipcu 2006 roku (cóż, baza znaków została wydana, a pełną specyfikację podano pod koniec 2006 roku). Obecna wersja to 10.0, która została opublikowana w czerwcu 2017 roku. Biorąc pod uwagę wzorzec wydany w ciągu ostatnich 4 lat, prawdopodobnie wersja 11.0 zostanie wydana w połowie 2018 roku.

    2. Dodano ważenie do wcześniej nieważonych znaków, które porównywałyby się jednakowo.

      Wagi te były bardziej niż prawdopodobne zdefiniowane w standardzie Unicode, ale nie w tej jego implementacji.

     
    Mimo to powyższa dokumentacja UCA jest dobrym miejscem do rozpoczęcia.

  2. Klawisze sortowania używane przez Windows / .NET / SQL Server nie są dokładnie takie same, jak pokazano w standardzie Unicode (patrz odpowiedź @ Patrick) lub zaimplementowane w OIOM . Aby zobaczyć, czego używają Windows / .NET / SQL Server, możesz wypróbować metodę CompareInfo.GetSortKey . Utworzyłem SQLCLR UDF, aby przekazać te wartości i uzyskać klucz sortowania. Należy pamiętać, że używam SQL Server 2017 na Windows 10 z zainstalowanym programem .NET Framework 4.5 - 4.6.1, więc .NET powinien używać Unicode 6.0.0. Poziom 4 nie jest również używany do tych ciągów.

    CHAR    L1     L2     L3     L4
    e      0E21
    E      0E21           12
    ë      0E21    13
    Ë      0E21    13     12

    Patrząc na te klucze sortowania dla Testu 1 i zdając sobie sprawę, że poziomy są sortowane jak wiele kolumn w ORDER BYklauzuli (L3 jest posortowane w obrębie tych samych wartości L2, które są posortowane w obrębie tych samych wartości L1), powinno zilustrować, że przyczyną takiego zachowania jest zauważono w pytaniu, że w rzeczywistości istnieje możliwość wielopoziomowego sortowania w Unicode. Również:

    CHAR       L1         L2       L3       L4
    EG      0E210E25              1212
    éH      0E210E2C      0E      0212
    ëh      0E210E2C      13
    ëH      0E210E2C      13      0212

    Patrząc na niektóre klucze sortowania dla Testu 2, widzimy, że znaki podstawowe są najpierw sortowane (L1), następnie akcentowane (L2), a następnie sortowane są obudowy (L3).

  3. Ponieważ typem danych jest NVARCHAR, zajmujemy się tylko punktami kodowymi Unicode i algorytmami sortowania, stąd użycie UNICODE()funkcji w TESTIE 1. Podczas gdy strony kodowe są określone przez większość zestawień, dotyczą one tylko VARCHARdanych. Oznacza to, że chociaż strona kodowa 1252 jest określona przez Latin1_General*serię zestawień, które można tutaj zignorować.

  4. Wagi opisane w Domyślnej tabeli elementów zestawiania Unicode (DUCET) ( wersja 5.0.0, która powinna być odwzorowana na _100_serie zestawień) są odpowiednie dla angielskiego języka amerykańskiego, ale nie dla innych ustawień regionalnych / języków. Inne języki muszą zaczynać się od DUCET, a następnie stosować reguły zastępowania specyficzne dla ustawień regionalnych, określone w projekcie Common Locale Data Repository (CLDR). Z tego, co mogę powiedzieć, wersje 1.4 / 1.4.1 zostały wydane w 2006 roku. Aby uzyskać te zastąpienia, pobierz plik „core” CLDR 1.4 przez http://unicode.org/Public/cldr/1.4.0/core.zip , a następnie w tym pliku zip przejdź do folderu sortowania i znajdź plik XML odpowiadający używanemu ustawieniu regionalnemu. Te pliki zawierają tylko przesłonięcia i nie są kompletnymi zestawami reguł zestawiania.

Solomon Rutzky
źródło