Làm thế nào một "ký tự" (có thể bao gồm nhiều Điểm Mã: cặp thay thế, kết hợp các ký tự, v.v.) so sánh với một ký tự khác dựa trên một bộ quy tắc khá phức tạp. Nó rất phức tạp do cần phải tính đến tất cả các quy tắc khác nhau (và đôi khi "lập dị") được tìm thấy trong tất cả các ngôn ngữ được trình bày trong đặc tả Unicode . Hệ thống này áp dụng cho Collation không nhị phân cho tất cả NVARCHAR
dữ liệu và cho VARCHAR
dữ liệu đang sử dụng Windows Collation chứ không phải SQL Server Collation (bắt đầu bằng SQL_
). Hệ thống này không áp dụng cho VARCHAR
dữ liệu bằng cách sử dụng SQL Server Collation vì những người này sử dụng ánh xạ đơn giản.
Hầu hết các quy tắc được xác định trong Thuật toán đối chiếu Unicode (UCA) . Một số quy tắc đó và một số quy định không được nêu trong UCA là:
- Thứ tự / trọng lượng mặc định được đưa ra trong
allkeys.txt
tệp (ghi chú bên dưới)
- Những độ nhạy và tùy chọn nào đang được sử dụng (ví dụ: trường hợp nhạy cảm hay không nhạy cảm?, Và nếu nhạy cảm, thì đó là chữ hoa hay chữ thường trước?)
- Bất kỳ ghi đè dựa trên miền địa phương.
- Phiên bản của tiêu chuẩn Unicode đang được sử dụng.
- Yếu tố "con người" (tức là Unicode là một đặc điểm kỹ thuật, không phải phần mềm và do đó tùy thuộc vào mỗi nhà cung cấp để thực hiện nó)
Tôi nhấn mạnh rằng điểm cuối cùng liên quan đến yếu tố con người để hy vọng làm rõ rằng người ta không nên mong đợi SQL Server luôn hành xử 100% theo đặc tả.
Yếu tố ghi đè ở đây là trọng số được trao cho mỗi Điểm Mã và thực tế là nhiều Điểm Mã có thể chia sẻ cùng một thông số trọng số. Bạn có thể tìm thấy các trọng số cơ bản (không có phần ghi đè cụ thể theo địa phương) tại đây (Tôi tin rằng 100
loạt Collations là Unicode v 5.0 - xác nhận không chính thức trong các nhận xét về mục Microsoft Connect ):
http://www.unicode.org/Public/UCA/5.0.0/allkeys.txt
Điểm Mã trong câu hỏi - U + FFFD - được định nghĩa là:
FFFD ; [*0F12.0020.0002.FFFD] # REPLACEMENT CHARACTER
Ký hiệu đó được định nghĩa trong phần 9.1 Định dạng tệp Allkey của UCA:
<entry> := <charList> ';' <collElement>+ <eol>
<charList> := <char>+
<collElement> := "[" <alt> <weight> "." <weight> "." <weight> ("." <weight>)? "]"
<alt> := "*" | "."
Collation elements marked with a "*" are variable.
Dòng cuối cùng đó rất quan trọng vì Điểm Mã mà chúng tôi đang xem có một đặc điểm kỹ thuật thực sự bắt đầu bằng "*". Trong phần 3.6 Biến trọng số có bốn hành vi có thể được xác định, dựa trên các giá trị cấu hình Collation mà chúng tôi không có quyền truy cập trực tiếp (chúng được mã hóa cứng vào triển khai Microsoft của mỗi Collation, chẳng hạn như phân biệt chữ hoa chữ thường sử dụng chữ thường chữ hoa trước, một thuộc tính khác nhau giữa VARCHAR
dữ liệu sử dụng SQL_
Collations và tất cả các biến thể khác).
Tôi không có thời gian để thực hiện nghiên cứu đầy đủ về các đường dẫn được thực hiện và suy ra các tùy chọn nào đang được sử dụng sao cho có thể đưa ra bằng chứng vững chắc hơn, nhưng có thể nói rằng trong mỗi đặc điểm kỹ thuật của Code Code, có hay không được coi là "bằng" sẽ không luôn luôn sử dụng các đặc điểm kỹ thuật đầy đủ. Trong trường hợp này, chúng tôi có "0F12.0020.0002.FFFD" và rất có thể nó chỉ là cấp 2 và 3 đang được sử dụng (ví dụ: 0,0020,0002. ). Thực hiện "Đếm" trong Notepad ++ cho ".0020.0002." tìm thấy 12.581 trận đấu (bao gồm cả các ký tự bổ sung mà chúng tôi chưa xử lý). Thực hiện "Đếm" trên "[*" sẽ trả về 4049 trận đấu. Thực hiện RegEx "Tìm" / "Đếm" bằng cách sử dụng mẫu\[\*\d{4}\.0020\.0002
trả về 832 trận đấu. Vì vậy, ở đâu đó trong sự kết hợp này, cộng với có thể một số quy tắc khác mà tôi không thấy, cộng với một số chi tiết triển khai cụ thể của Microsoft, là lời giải thích đầy đủ về hành vi này. Và rõ ràng, hành vi là giống nhau cho tất cả các nhân vật phù hợp vì tất cả các nhân vật phù hợp với nhau vì tất cả đều có cùng trọng lượng một khi các quy tắc được áp dụng (có nghĩa là, câu hỏi này có thể được hỏi về bất kỳ ai trong số họ, không phải nhất thiết là ông �
).
Bạn có thể thấy với truy vấn bên dưới và thay đổi COLLATE
mệnh đề theo kết quả bên dưới truy vấn về cách thức các độ nhạy khác nhau hoạt động trên hai phiên bản Collations:
;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;
Số lượng khác nhau của các nhân vật phù hợp tại các bộ sưu tập khác nhau dưới đây.
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
Trong tất cả các đối chiếu được liệt kê ở trên N'' = N'�'
cũng đánh giá là đúng.
CẬP NHẬT
Tôi đã có thể nghiên cứu thêm một chút và đây là những gì tôi tìm thấy:
Làm thế nào nó "có thể" nên làm việc
Sử dụng ICU Collation Demo , tôi đặt ngôn ngữ thành "en-US-u-va-posix", đặt độ mạnh thành "chính", kiểm tra hiển thị "khóa sắp xếp" và dán vào 4 ký tự sau mà tôi đã sao chép từ kết quả của truy vấn trên (sử dụng Latin1_General_100_CI_AI
Collation):
�
Ԩ
ԩ
Ԫ
và điều đó trả về:
Ԫ
60 2E 02 .
Ԩ
60 7A .
ԩ
60 7A .
�
FF FD .
Sau đó, kiểm tra các thuộc tính ký tự cho "" tại http://unicode.org/cldr/utility/character.jsp?a=fffd và xem khóa sắp xếp cấp 1 (nghĩa là FF FD
) khớp với thuộc tính "uca". Nhấp vào thuộc tính "uca" đó sẽ đưa bạn đến trang tìm kiếm - http://unicode.org/cldr/utility/list-unicodeset.jsp?a=%5B%3Auca%3DFFFD%3A%5D - chỉ hiển thị 1 trận đấu. Và, trong tệp allkeys.txt , trọng số sắp xếp cấp 1 được hiển thị là 0F12
và chỉ có 1 kết quả khớp cho điều đó.
Để chắc chắn rằng chúng tôi đang diễn giải chính xác hành vi, tôi đã xem xét một nhân vật khác: GREEK VỐN VỐN OMICRON VỚI VARIA Ὸ
tại http://unicode.org/cldr/utility/character.jsp?a=1FF8 có "uca" ( tức là cấp 1 sắp xếp trọng lượng / phần tử đối chiếu) của 5F30
. Nhấp vào "5F30" đó sẽ đưa chúng ta đến trang tìm kiếm - http://unicode.org/cldr/utility/list-unicodeset.jsp?a=%5B%3Auca%3D5F30%3A%5D - hiển thị 30 trận đấu, 20 trong số chúng nằm trong phạm vi 0 - 65535 (tức là U + 0000 - U + FFFF). Nhìn vào tệp allkeys.txt cho Mã điểm 1FF8 , chúng tôi thấy trọng số sắp xếp cấp 1 là 12E0
. Thực hiện "Đếm" trong Notepad ++ trên12E0.
hiển thị 30 kết quả khớp (điều này khớp với kết quả từ Unicode.org, mặc dù điều đó không được đảm bảo vì tệp dành cho Unicode v 5.0 và trang web đang sử dụng dữ liệu Unicode v 9.0).
Trong SQL Server, truy vấn sau đây trả về 20 kết quả trùng khớp, giống như tìm kiếm Unicode.org khi xóa 10 ký tự bổ sung:
;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;
Và, để chắc chắn, hãy quay lại trang Demo Collation của ICU và thay thế các ký tự trong hộp "Đầu vào" bằng 3 ký tự sau được lấy từ danh sách 20 kết quả từ SQL Server:
Ὂ
𝜪
Ὸ
thực tế cho thấy tất cả chúng đều có cùng 5F 30
trọng số sắp xếp cấp 1 (khớp với trường "uca" trên trang thuộc tính ký tự).
VÌ VẬY, có vẻ như nhân vật đặc biệt này không nên phù hợp với bất cứ điều gì khác.
Làm thế nào nó thực sự hoạt động (ít nhất là trong Microsoft-đất)
Không giống như bên trong SQL Server, .NET có một phương tiện hiển thị khóa sắp xếp cho một chuỗi thông qua Phương thức CompareInfo.GetSortKey . Sử dụng phương thức này và chỉ nhập ký tự U + FFFD, nó trả về một khóa sắp xếp của 0x0101010100
. Sau đó, lặp lại tất cả các ký tự trong phạm vi 0 - 65535 để xem những ký tự nào có khóa sắp xếp 0x0101010100
trả về 4529 trận đấu. Điều này không khớp chính xác với 5840 được trả về trong SQL Server (khi sử dụng Latin1_General_100_CS_AS_WS
Collation), nhưng đây là lần gần nhất chúng tôi có thể nhận được (hiện tại) vì tôi đang chạy Windows 10 và .NET Framework phiên bản 4.6.1, sử dụng Unicode v 6.3.0 theo biểu đồ cho Lớp CharUnicodeInfo(trong "Lưu ý cho người gọi", trong phần "Ghi chú"). Hiện tại tôi đang sử dụng hàm SQLCLR và do đó không thể thay đổi phiên bản Khung đích. Khi có cơ hội, tôi sẽ tạo một ứng dụng bảng điều khiển và sử dụng phiên bản Khung mục tiêu 4.5 vì sử dụng Unicode v 5.0, phù hợp với Bộ sưu tập 100 loạt.
Điều mà thử nghiệm này cho thấy là, ngay cả khi không có cùng số lượng trùng khớp chính xác giữa .NET và SQL Server cho U + FFFD, thì rõ ràng đây không phải là hành vi dành riêng cho SQL Server và cho dù việc thực hiện có chủ ý hay giám sát việc thực hiện đã được thực hiện hay không bởi Microsoft, ký tự U + FFFD thực sự khớp với khá nhiều ký tự, ngay cả khi nó không theo đặc tả Unicode. Và, do nhân vật này phù hợp với U + 0000 (null), có lẽ đó chỉ là vấn đề thiếu trọng lượng.
CŨNG THẾ
Về sự khác biệt trong hành vi trong =
truy vấn so với LIKE N'%�%'
truy vấn, nó phải liên quan đến các ký tự đại diện và trọng số (tôi giả sử) bị thiếu cho các � Ƕ Ƿ Ǹ
ký tự (tức là ) này. Nếu LIKE
điều kiện được thay đổi thành đơn giản LIKE N'�'
thì nó trả về 3 hàng giống như =
điều kiện. Nếu sự cố với ký tự đại diện không phải do trọng số "thiếu" (không có 0x00
khóa sắp xếp nào được trả về bởi CompareInfo.GetSortKey
btw) thì đó có thể là do các ký tự này có khả năng có thuộc tính cho phép khóa sắp xếp khác nhau tùy theo ngữ cảnh (ví dụ: các ký tự xung quanh ).
FFFD
(tìm kiếm*0F12.0020.0002.FFFD
chỉ trả về một kết quả). Từ quan sát của @ Forrest rằng tất cả chúng đều khớp với chuỗi rỗng và đọc thêm một chút về chủ đề, có vẻ như trọng lượng họ chia sẻ trong các đối chiếu không nhị phân khác nhau thực tế là không.