เน้นการเรียงที่ละเอียดอ่อน


19

ทำไมทั้งสองSELECTคำสั่งจึงส่งผลให้เรียงลำดับที่แตกต่างกัน

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║ - ควรเป็น 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║
╚════╩══════╩══════╝

คำตอบ:


13

คำถามนี้ไม่เกี่ยวข้องกับฐานข้อมูล แต่เพิ่มเติมเกี่ยวกับการจัดการ Unicode และกฎ

ขึ้นอยู่กับhttps://docs.microsoft.com/en-us/sql/t-sql/statements/windows-collation-name-transact-sql Latin1_General_100_CS_ASหมายถึง "การจัดเรียงใช้กฎการเรียงลำดับพจนานุกรม Latin1 ทั่วไปและแผนที่ไปยังหน้ารหัส 1252 "ที่มี CS = ตัวพิมพ์เล็กและตัวพิมพ์ใหญ่ AS = Accent Sensitive

การแมประหว่างรหัส Windows หน้า 1252 และ Unicode ( http://www.unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/CP1252.TXT ) จะแสดงค่าเดียวกันสำหรับอักขระทั้งหมดที่เรากำลังจัดการกับ (ยกเว้น e กับ macron ที่ไม่มีอยู่ในการแมปของ Microsoft ดังนั้นจึงไม่ทราบว่ามันเกิดอะไรขึ้นกับกรณีนี้) ดังนั้นเราจึงสามารถมุ่งเน้นไปที่เครื่องมือและคำศัพท์ Unicode ในตอนนี้

ก่อนอื่นให้เรารู้อย่างแม่นยำถึงสิ่งที่เรากำลังทำอยู่

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

อัลกอริทึมการจัดเรียง Unicode อธิบายไว้ที่นี่: https://www.unicode.org/reports/tr10/

ดูที่หัวข้อ 1.3 "ความไวตามบริบท" ซึ่งอธิบายว่าการเรียงลำดับไม่สามารถขึ้นอยู่กับอักขระเพียงตัวเดียวหลังจากกฎอื่น ๆ เนื่องจากกฎบางอย่างมีความอ่อนไหวตามบริบท

โปรดทราบว่าคะแนนเหล่านี้ใน 1.8:

การเรียงหน้าไม่ใช่คุณสมบัติของสตริง ลำดับการเรียงจะไม่ถูกสงวนไว้ภายใต้การดำเนินการเรียงต่อกันหรือซับสตริงโดยทั่วไป

ตามค่าเริ่มต้นอัลกอริทึมใช้สามระดับที่ปรับแต่งได้อย่างเต็มที่ สำหรับสคริปต์ละตินระดับเหล่านี้จะสัมพันธ์กับ:

alphabetic ordering
diacritic ordering
case ordering.

แต่อัลกอริทึมด้วยตัวเองค่อนข้างหนาแน่น ส่วนสำคัญของมันคือ: ระบุไว้สั้น ๆ อัลกอริทึมการจัดเรียง Unicode ใช้สายอักขระ Unicode การป้อนข้อมูลและตารางองค์ประกอบการเรียงซึ่งประกอบด้วยข้อมูลการแมปสำหรับอักขระ มันสร้างคีย์การเรียงลำดับซึ่งเป็นอาร์เรย์ของจำนวนเต็ม 16 บิตที่ไม่ได้ลงนาม อย่างน้อยสองคีย์การเรียงลำดับที่ถูกสร้างขึ้นดังนั้นสามารถเปรียบเทียบแบบไบนารี่เพื่อให้การเปรียบเทียบที่ถูกต้องระหว่างสตริงที่ถูกสร้างขึ้น

คุณสามารถดูกฎการเรียงลำดับภาษาละตินได้ที่นี่: http://developer.mimer.com/collations/charts/latin.htm หรือมากกว่านั้นโดยตรงและโดยเฉพาะสำหรับ MS SQL: http://collation-charts.org/mssql/mssql 0409.1252.Latin1_General_CS_AS.html

สำหรับeตัวละครมันแสดงให้เห็น:

e E éÉèÈêÊëË

สิ่งนี้จะอธิบายผลลัพธ์ของคุณเมื่อสั่งซื้อcol1ยกเว้นว่าēไม่มีอยู่ในรหัสหน้า 1252 ดังนั้นฉันจึงไม่ทราบเลยว่ามันทำอะไรกับมัน

หรือถ้าเราใช้อัลกอริธึม Unicode ด้วยตนเองให้ใช้ค่าคีย์ของ DUCET ที่http://www.unicode.org/Public/UCA/latest/allkeys.txt :

ขั้นตอนที่ 1: รูปแบบการทำให้เป็นมาตรฐาน D ดังนั้นแต่ละกรณีจะกลายเป็น:

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

ขั้นตอนที่ 2 สร้างอาร์เรย์การเรียง (ค้นหาในไฟล์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]

ขั้นตอนที่ 3, ปุ่มเรียงแบบฟอร์ม (สำหรับแต่ละระดับ, ใช้แต่ละค่าภายในแต่ละแถวลำดับแล้วใส่ 0000 เป็นตัวคั่นและเริ่มต้นอีกครั้งสำหรับระดับถัดไป)

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

ขั้นตอนที่ 4 เปรียบเทียบคีย์การเรียงลำดับ (การเปรียบเทียบไบนารีอย่างง่ายของแต่ละค่าหนึ่งต่อหนึ่ง): ค่าที่สี่เพียงพอที่จะเรียงลำดับทั้งหมดดังนั้นลำดับสุดท้ายจึงกลายเป็น:

e
é
è
ê
ë
ē

ในทำนองเดียวกันสำหรับการสั่งซื้อในcol2:

ขั้นตอนที่ 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

ขั้นตอนที่ 2: อาร์เรย์ Collation

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]

ขั้นตอนที่ 3: ปุ่มจัดเรียงแบบฟอร์ม

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

ขั้นตอนที่ 4: เปรียบเทียบคีย์การเรียงลำดับ: ค่าที่สองนั้นเพียงพอที่จะจัดเรียงทั้งหมดและเป็นความจริงแล้วในการเพิ่มลำดับดังนั้นลำดับสุดท้ายคือ:

eA
éB
ëC
èD
êE
ēF

อัปเดต : เพิ่มโซโลมอน Rutzky กรณีที่สามซึ่งมีเล่ห์เหลี่ยมเนื่องจากพื้นที่ที่เปิดใช้งานกฎใหม่ (ฉันเลือก "กรณีที่ไม่สนใจ"):

ขั้นตอนที่ 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

ขั้นตอนที่ 2 สร้างอาร์เรย์การเรียง:

è 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]

ขั้นตอนที่ 3 คีย์การเรียงแบบฟอร์ม:

è 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

ขั้นตอนที่ 4 เปรียบเทียบคีย์การเรียงลำดับ:

โดยพื้นฐานแล้วค่าที่สามเป็นตัวกำหนดคำสั่งและในความเป็นจริงจะขึ้นอยู่กับตัวเลขสุดท้ายเท่านั้นดังนั้นคำสั่งควรเป็น:

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

อัปเดตที่สองตามความคิดเห็นของโซโลมอน Rutzky เกี่ยวกับรุ่น Unicode

ฉันใช้allkeys.txtข้อมูลเกี่ยวกับ Unicode รุ่นล่าสุดในเวลานี้นั่นคือรุ่น 10.0

หากเราจำเป็นต้องคำนึงถึงUnicode 5.1แทนสิ่งนี้จะเป็น: http://www.unicode.org/Public/UCA/5.1.0/allkeys.txt

ฉันเพิ่งตรวจสอบสำหรับตัวละครทั้งหมดข้างต้นอาร์เรย์การเรียงมีดังต่อไปนี้แทน:

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]

และ:

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]

และ:

è 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]

ซึ่งคำนวณให้กับคีย์การเรียงลำดับต่อไปนี้:

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

และ:

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

และ:

è 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

ซึ่งให้ผลการเรียงข้อมูลทั้งสามนี้อีกครั้ง:

e
é
è
ê
ë
ē

และ

eA
éB
ëC
èD
êE
ēF

และ

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

สวัสดีแพทริค ขอบคุณสำหรับการโพสต์ข้อมูลรายละเอียดนี้ หมายเหตุเล็ก ๆ น้อย ๆ : 1)คุณสามารถละเว้นรหัสหน้า 1252 สำหรับข้อมูลVARCHAR(เช่นที่ไม่ใช่ Unicode) ซึ่งไม่ได้ใช้งานที่นี่ นั่นคือเหตุผลที่ēตัวละครใช้งานได้ดี 2) "การเปรียบเทียบแผนภูมิ" ข้อมูลเป็นบิตล้าสมัย มันมีไว้สำหรับ Collation รุ่นก่อนหน้านี้และพวกเขาไม่ได้โพสต์อะไรตั้งแต่ปี 2009 3) Unicode version ที่นี่ไม่ใช่รุ่นล่าสุด (เวอร์ชั่น 10) _100_ชุด Collations มาพร้อมกับ SQL 2008 ดังนั้นนี้จะเป็น Unicode 5.0 หรือ 5.1: unicode.org/standard/versions/#TUS_Earlier_Versions
โซโลมอน Rutzky

ฉันไม่คิดว่าการallKeys.txt เปลี่ยนแปลงระหว่าง Unicode เวอร์ชันนอกเหนือจากการเพิ่มตัวละครใหม่ดังนั้นข้างต้นควรเป็นจริง แต่แน่นอนว่ามันสามารถทำซ้ำได้กับข้อมูลเก่าก่อนหน้านี้ฉันแค่ขาดพลังงานในการใส่ลงไปอีกหลายชั่วโมง สำหรับ CP1252 เป็นเพียงมาจากคำนิยามที่กำหนดโดย MS-SQL (ฉันไม่ได้ใช้ผลิตภัณฑ์นี้ด้วยตัวเอง)
Patrick Mevzek

1)อาจไม่มีการเปลี่ยนแปลงที่สำคัญระหว่างเวอร์ชัน แต่ฉันค่อนข้างแน่ใจว่ามีการแก้ไขน้ำหนัก / การจำแนกประเภทอย่างน้อยที่สุด แต่ใช่ฉันได้รับข้อ จำกัด เวลาอย่างแน่นอน;) 2)เกี่ยวกับ CP1252 ฉันพูดถึงมันเพราะแนวคิดของ Code Pages ไม่มีอยู่ใน Unicode Unicode เป็นวิธีการที่ไม่ต้องใช้โค้ดเพจ เอกสาร MS นั้นไม่ชัดเจนในเรื่องนี้ แต่จะกล่าวถึงด้านบน " หน้ารหัสที่ใช้ในการจัดเก็บข้อมูลอักขระที่ไม่ใช่ Unicode " คุณถูกต้องแล้วว่าตัวละครตัวหนึ่งไม่ได้อยู่ใน CP1252 แต่ Code Pages ไม่ได้เล่นที่นี่
โซโลมอน Rutzky

ใช่ฉันไม่เคยพูดอะไรเกี่ยวกับโค้ดเพจที่เกี่ยวข้องกับ Unicode มันเป็นเพียงเอกสารประกอบ MS SQL ที่ระบุว่าชื่อการเรียงนี้ใช้งานได้กับ "รหัสหน้า 1252" มันอาจจะไม่มีเหตุผล (มันไม่ได้สำหรับฉัน แต่ก็ไม่ใช่สำหรับผู้ใช้) แต่มันเป็นสิ่งที่เขียนไว้ในเอกสารประกอบของซอฟต์แวร์นี้ สำหรับการทำซ้ำงานรู้สึกฟรีหรือเพียงแค่ให้การเปลี่ยนแปลงเกี่ยวกับการเรียงลำดับที่เกี่ยวข้องกับตัวละครในการเล่นที่นี่ระหว่าง Unicode 5 และล่าสุดหากมีและถ้าคุณต้องการ ฉันเชื่อว่าคำตอบของฉันเป็นไปอย่างแม่นยำและสร้างผลลัพธ์ตามอินพุตไม่ใช่วิธีอื่น
Patrick Mevzek

3)อีกครั้ง: ประโยคที่ 1: ในขณะที่ส่วนใหญ่เกี่ยวกับ Unicode คำถามนี้เป็นปัญหาของระบบปฏิบัติการเนื่องจาก MS ใช้งานข้อมูลจำเพาะและอาจไม่ได้ทำทั้งหมดและ / หรืออาจทำผิดพลาด ฉันพบข้อผิดพลาด 2 รายการใน. NET เกี่ยวกับวิธีการกำหนด "หมวดหมู่" หรือ "บล็อก" ที่มีอักขระเฉพาะอยู่ในนั้น นอกจากนี้เป็นอย่างน้อยเล็กน้อยปัญหา SQL Server เท่านั้นเนื่องจาก SQL Server มีประสิทธิภาพภาพรวมของแต่ละเปรียบเทียบที่จุดในเวลา (สำหรับความสอดคล้องระหว่างรุ่น) เพื่อพวกเขาจะได้อะไรคือตอนนี้ SQL Server ของพฤติกรรมที่เฉพาะเจาะจง
โซโลมอน Rutzky

16

พฤติกรรมที่คุณเห็นอยู่ที่นี่มีสาเหตุมาจากความจริงที่ว่าอัลกอริธึมการเรียง Unicode (UCA)ช่วยให้การเรียงลำดับที่ซับซ้อนและหลายระดับ โดยเฉพาะอย่างยิ่ง:

  1. การเรียงลำดับไม่ใช่การเปรียบเทียบ:

    การพิจารณาว่าสองสายเหมือนกันหรือแตกต่างกันค่อนข้างตรงไปข้างหน้า (กำหนดสถานที่ / ภาษาเฉพาะและชุดของความไว) แต่การพิจารณาลำดับของ 2 หรือมากกว่านั้นอาจมีความซับซ้อนสูง

  2. การเรียงลำดับจะทำในชุดของขั้นตอนโดยแต่ละขั้นตอนจะนำไปใช้กับสตริงทั้งหมดไม่ใช่ตัวอักษรต่ออักขระ:

    1. มาตรฐาน: จัดเรียงอักขระพื้นฐาน (ไม่คำนึงถึงความแตกต่างของสำเนียงและตัวพิมพ์เล็ก)
    2. หากมีความอ่อนไหวต่อสำเนียงให้ใช้น้ำหนักสำเนียง / การกำกับเสียง
    3. หากเป็นกรณี ๆ ไปให้ใช้ตุ้มน้ำหนักตัวเรือน

เมื่อคุณจัดเรียงตามcol1(อักขระเดียว) อันดับแรกจะพิจารณาว่าตัวละครทั้งหมดมีน้ำหนักเท่ากันเนื่องจากเป็น " e " ทั้งหมด ถัดไปจะใช้น้ำหนักสำเนียง / การออกเสียง ไม่มีความแตกต่างของตัวเรือนดังนั้นขั้นตอนที่สามจะไม่เปลี่ยนแปลงอะไรเลย ดังนั้นความแตกต่างเพียง แต่อยู่ในขั้นตอนที่ 2 col1ซึ่งเป็นเหตุผลที่จะมีคำสั่งที่แนะนำสำหรับแถวเหล่านั้นขึ้นอยู่กับ

เมื่อคุณเรียงลำดับตามcol2(อักขระสองตัว) อันดับแรกจะกำหนดว่าแต่ละแถวมีน้ำหนักแตกต่างกันเนื่องจากตัวละครทั้งสองถูกใช้เพื่อกำหนดน้ำหนักการเรียงลำดับ (เช่น " ea ", " eb " ฯลฯ ) ถัดไปจะใช้น้ำหนักสำเนียง / การออกเสียง ไม่มีความแตกต่างของตัวเรือนดังนั้นขั้นตอนที่สามจะไม่เปลี่ยนแปลงอะไรเลย ดังนั้นจึงมีความแตกต่างในขั้นตอนที่ 1 และ 2 ในครั้งนี้ แต่เนื่องจากความแตกต่างในขั้นตอนที่ 1 ได้ถูกนำไปใช้กับแต่ละสตริงแล้วก่อนที่จะพิจารณาน้ำหนักของขั้นตอนที่ 2 ดังนั้นน้ำหนักจากขั้นตอนที่ 2 จึงไม่มีผลต่อการสั่งซื้อ พวกเขาจะใช้เฉพาะถ้าน้ำหนักจากขั้นตอนที่ 1 สำหรับสองแถวหรือมากกว่านั้นเหมือนกัน

การปรับตัวอย่างโค้ดต่อไปนี้จากคำถามหวังว่าจะแสดงพฤติกรรมการเรียงลำดับที่อธิบายไว้ข้างต้น ฉันเพิ่มแถวเพิ่มเติมบางส่วนและคอลัมน์เพิ่มเติมเพื่อช่วยแสดงผลกระทบของการจัดเรียงที่คำนึงถึงตัวพิมพ์เล็กและตัวพิมพ์ใหญ่ (เนื่องจากข้อมูลตัวอย่างดั้งเดิมเป็นตัวพิมพ์เล็กทั้งหมด):

ติดตั้ง

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');

ทดสอบ 1

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

ผลตอบแทน:

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

สิ่งที่เราเห็นในผลลัพธ์ด้านบน:

  1. จุดรหัสไม่ได้กำหนดลำดับการจัดเรียง
  2. อักขระที่ไม่เน้นเสียงจะถูกจัดเรียงก่อนอักขระเน้นเสียง (ภายในตัวอักษรเดียวกัน: fจะยังคงอยู่หลังจากทั้งหมดเหล่านี้) ชัดเจนว่ามีการใช้น้ำหนักสำเนียงก่อนน้ำหนักตัวพิมพ์เล็ก
  3. เรียงตัวพิมพ์เล็กก่อนหน้าตัวพิมพ์ใหญ่ภายในอักขระที่เน้นเสียง (หรือไม่เน้นเสียง) เดียวกัน (เช่นeแล้วEและEและëจากนั้นË ) การตัดเย็บนี้ใช้โดย Windows Collations ส่วนใหญ่ในขณะที่ SQL Server Collations ส่วนใหญ่จะเรียงลำดับตัวพิมพ์ใหญ่ก่อน

ทดสอบ 2

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

ผลตอบแทน:

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

สิ่งที่เราเห็นในผลลัพธ์ด้านบน:

  1. การเรียงลำดับระดับแรกอย่างแท้จริงคืออักขระพื้นฐาน หากเป็นการเน้นเสียง / กำกับออกเสียงดังนั้นëC (id = 4), ēF (id = 10), และEG (id = 5) แถวจะไม่อยู่ตรงไหน ถ้ามันเป็นปลอกแล้วแถวEG (id = 5) จะไม่เป็นที่ที่มันอยู่
  2. การเรียงลำดับระดับที่สองอย่างแท้จริงคือการเน้นเสียง / การกำกับเสียง สิ่งนี้อธิบายว่าทำไมสามแถวสุดท้ายคือéH -> ëh -> ëHแทนที่จะเป็นëh -> éH -> ëH (เช่น IDs 3 -> 6 -> 9 แทนที่จะเป็น 6 -> 3 -> 9)
  3. การเรียงลำดับระดับที่สามอย่างแท้จริงคือตัวเรือน นี่คือสาเหตุที่ 2 แถวสุดท้ายคือëh -> ëHเนื่องจากเรียงตัวพิมพ์เล็กก่อน

ทดสอบ 3

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

ผลตอบแทน:

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

สิ่งที่เราเห็นในผลลัพธ์ด้านบน:

  1. ลำดับการจัดเรียงนั้นเหมือนกับในการทดสอบ 2 ความแตกต่างเพียงอย่างเดียวในค่าทดสอบที่นี่คือมีช่องว่างระหว่างตัวละครแต่ละตัวซึ่งจะลบความเป็นไปได้ของกฎบริบท ดังนั้นเรารู้ว่าสาเหตุของความแตกต่างในการเรียงลำดับcol2ของคำถามนั้นเกิดขึ้นอีกครั้งเนื่องจาก "การเปรียบเทียบหลายระดับ" และไม่ใช่ "ความไวตามบริบท"

หมายเหตุเพิ่มเติม:

  1. เกี่ยวกับการรับกฎที่แน่นอนนั่นไม่ใช่เรื่องง่ายอย่างที่ควรจะเป็น ปัญหาเกี่ยวกับการอธิบายอย่างเป็นรูปธรรมของกฎเหล่านี้คือกฎการเรียงลำดับ Unicode ในขณะที่จัดทำเอกสารแน่นอนเป็นข้อเสนอแนะ ขึ้นอยู่กับผู้ขายเช่น Microsoft เพื่อดำเนินการตามคำแนะนำเหล่านั้น Microsoft ไม่ได้ใช้คำแนะนำตามที่ระบุไว้ในเอกสาร Unicode ดังนั้นจึงมีการยกเลิกการเชื่อมต่อที่นั่น (คล้ายกับว่าข้อกำหนด HTML หรือ CSS ไม่ได้ถูกนำไปใช้อย่างสมบูรณ์หรือแม้แต่ในลักษณะเดียวกันกับผู้ขาย) จากนั้นมี Windows Collations รุ่นต่าง ๆ (คุณใช้รุ่น100ที่ออกมาพร้อมกับ SQL Server 2008) และเชื่อมโยงกับ Unicode รุ่นที่เก่ากว่า Unicode รุ่นปัจจุบันหรือICU Collation Demoรุ่นปัจจุบันกำลังใช้งาน ตัวอย่างเช่นมีอะไรใหม่ในส่วนการเปรียบเทียบ SQL Server 2008ของเอกสาร"Collation และ Unicode Support" สำหรับ SQL Server 2008ทำให้ 2 ประเด็นที่น่าสนใจมากเกี่ยวกับสิ่งที่ "ใหม่" ในการจัด_100_เรียงชุดข้อมูล:

    1. ตารางกรณี Unicode 5.0

      Unicode 5.0 ได้รับการตีพิมพ์ในเดือนกรกฎาคมของปี 2549 (ฐานข้อมูลตัวละครได้รับการเผยแพร่แล้วและข้อมูลจำเพาะทั้งหมดตามมาในปลายปี 2549) เวอร์ชั่นปัจจุบันคือ 10.0 ซึ่งตีพิมพ์ในเดือนมิถุนายนปี 2560 และจากรูปแบบการเปิดตัวของ 4 ปีที่ผ่านมามีแนวโน้มว่ารุ่น 11.0 จะออกวางจำหน่ายในช่วงกลางปี ​​2018

    2. การเพิ่มน้ำหนักถูกเพิ่มไปยังอักขระที่ไม่ใช่น้ำหนักก่อนหน้าซึ่งจะมีการเปรียบเทียบอย่างเท่าเทียมกัน

      น้ำหนักเหล่านั้นมากกว่าที่กำหนดไว้ในมาตรฐาน Unicode ไม่ใช่แค่การนำไปปฏิบัติ

     
    ถึงกระนั้นเอกสาร UCA ที่ลิงก์ด้านบนยังเป็นจุดเริ่มต้นที่ดี

  2. เรียง Keys ใช้โดย Windows / .net / SQL Server จะไม่ตรงเช่นเดียวกับที่ปรากฏในมาตรฐาน Unicode (ดูคำตอบ @ แพททริค) หรือการใช้งานในห้องไอซียู หากต้องการดูสิ่งที่ Windows / .net / SQL Server ใช้งานคุณสามารถลองวิธี CompareInfo.GetSortKey ฉันสร้าง SQLCLR UDF เพื่อส่งผ่านค่าเหล่านี้และรับคีย์การเรียงลำดับ โปรดทราบว่าฉันกำลังใช้ SQL Server 2017 บน Windows 10 ที่ติดตั้ง. NET Framework 4.5 - 4.6.1 ดังนั้น. NET ควรใช้ Unicode 6.0.0 นอกจากนี้ Level4 ไม่ถูกใช้สำหรับสตริงเหล่านี้

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

    ดูที่คีย์การเรียงลำดับเหล่านี้สำหรับการทดสอบ 1 และตระหนักว่าระดับจะถูกจัดเรียงเหมือนหลายคอลัมน์ภายในส่วนORDER BYคำสั่ง (L3 ถูกจัดเรียงภายในค่าเดียวกันของ L2 ซึ่งเรียงลำดับภายในค่าเดียวกันของ L1) ควรแสดงเหตุผลของพฤติกรรม ความจริงแล้วความสามารถในการเรียงลำดับหลายระดับของ Unicode ในทำนองเดียวกัน:

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

    เมื่อดูที่คีย์การเรียงลำดับสำหรับการทดสอบ 2 เราจะเห็นได้ว่าอักขระพื้นฐานถูกเรียงลำดับก่อน (L1) จากนั้นจะเรียงเสียงเน้นเสียง (L2) จากนั้นจะเรียงตัวเรียงลำดับ (L3)

  3. เนื่องจากประเภทข้อมูลนั้นNVARCHARเราเกี่ยวข้องเฉพาะกับ Unicode Code Points และการเรียงลำดับอัลกอริธึมเท่านั้นดังนั้นการใช้UNICODE()ฟังก์ชันใน TEST 1 ในขณะที่ Code Pages นั้นถูกระบุโดย Collation ส่วนใหญ่พวกมันเกี่ยวข้องกับVARCHARข้อมูลเท่านั้น ความหมายในขณะที่รหัสหน้า 1252 ถูกระบุโดยLatin1_General*ชุดของการเปรียบเทียบที่สามารถละเว้นได้ที่นี่

  4. น้ำหนักที่อธิบายไว้ในตารางองค์ประกอบการจัดเรียง Unicode เริ่มต้น (DUCET) ( เวอร์ชัน 5.0.0ซึ่งควรแมปกับการจัด_100_เรียงชุดข้อมูล) นั้นดีสำหรับภาษาอังกฤษแบบสหรัฐอเมริกา แต่ไม่ใช่ภาษา / ภาษาอื่น ๆ ภาษาอื่น ๆ ต้องเริ่มต้นด้วย DUCET จากนั้นใช้กฎการแทนที่เฉพาะสถานที่ตามที่กำหนดโดยโครงการ Common Locale Data Repository (CLDR) จากสิ่งที่ฉันสามารถบอกได้ว่ารุ่น 1.4 / 1.4.1 ได้รับการปล่อยตัวในปี 2549 หากต้องการรับการแทนที่เหล่านั้นให้ดาวน์โหลดไฟล์ "core" หลักของ CLDR 1.4 ผ่านhttp://unicode.org/Public/cldr/1.4.0/core.zipจากนั้นในไฟล์ zip ให้ไปที่โฟลเดอร์collationและค้นหาไฟล์ XML ที่สอดคล้องกับโลแคลที่ใช้ ไฟล์เหล่านั้นมีเพียงการแทนที่และไม่ใช่ชุดของกฎการเปรียบเทียบที่สมบูรณ์

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.