Tại sao việc tìm kiếm THÍCH N '%%' khớp với bất kỳ ký tự Unicode nào và = N' 'khớp với nhiều ký tự?


20
DECLARE @T TABLE(
  Col NCHAR(1));

INSERT INTO @T
VALUES      (N'A'),
            (N'B'),
            (N'C'),
            (N'Ƕ'),
            (N'Ƿ'),
            (N'Ǹ');

SELECT *
FROM   @T
WHERE  Col LIKE N'%�%'

Trả về

Col
A
B
C
Ƕ
Ƿ
Ǹ

SELECT *
FROM   @T
WHERE  Col = N'�' 

Trả về

Col
Ƕ
Ƿ
Ǹ

Tạo mọi "ký tự" byte kép có thể với bên dưới cho thấy =phiên bản khớp với 21.229 trong số chúng và LIKE N'%�%'tất cả phiên bản của chúng (Tôi đã thử một vài đối chiếu không nhị phân có cùng kết quả).

WITH T(I, N)
AS 
(
SELECT TOP 65536 ROW_NUMBER() OVER (ORDER BY @@SPID),
                 NCHAR(ROW_NUMBER() OVER (ORDER BY @@SPID))
FROM master..spt_values v1, 
     master..spt_values v2
)
SELECT I, N 
FROM T
WHERE N = N'�'  

Bất cứ ai cũng có thể làm sáng tỏ những gì đang xảy ra ở đây?

Sử dụng COLLATE Latin1_General_BINsau đó khớp với ký tự đơn NCHAR(65533)- nhưng câu hỏi là để hiểu nó sử dụng quy tắc nào trong trường hợp khác. Điều gì đặc biệt về 21.229 ký tự phù hợp với =và tại sao mọi thứ khớp với ký tự đại diện như thế nào? Tôi đoán có một số lý do đằng sau nó mà tôi đang thiếu.

nchar(65534)[và 21k người khác] cũng hoạt động tốt như vậy nchar(65533). Câu hỏi đặt ra có thể đã được phrased sử dụng nchar(502) bình đẳng như - nó cư xử như nhau cả hai như là LIKE N'%Ƕ%'(diêm tất cả mọi thứ) và trong =trường hợp này. Đó có lẽ là một đầu mối lớn.

Thay đổi SELECTtruy vấn cuối cùng để SELECT I, N, RANK() OVER(ORDER BY N)cho thấy rằng SQL Server không thể xếp hạng các ký tự. Dường như bất kỳ nhân vật nào không được xử lý bởi đối chiếu đều được coi là tương đương.

Một cơ sở dữ liệu với Latin1_General_100_CS_ASđối chiếu tạo ra 5840 kết quả khớp. Latin1_General_100_CS_AScắt giảm các =trận đấu khá đáng kể, nhưng không thay đổi LIKEhành vi. Dường như có một nhóm các ký tự đã nhỏ hơn trong các bộ sưu tập sau đó, tất cả đều so sánh bằng nhau và bị bỏ qua trong LIKEcác tìm kiếm ký tự đại diện sau đó.

Tôi đang sử dụng SQL Server 2016. Biểu tượng là ký tự thay thế Unicode, nhưng các ký tự không hợp lệ duy nhất trong mã hóa UCS-2 là 55296 - 57343 AFAIK và nó phù hợp rõ ràng với các điểm mã hợp lệ hoàn toàn như N'Ԛ'trong phạm vi này.

Tất cả các ký tự này hoạt động giống như chuỗi rỗng cho LIKE=. Họ thậm chí đánh giá là tương đương. N'' = N'�'là đúng, và bạn có thể thả nó trong một LIKEso sánh các không gian đơn lẻ không LIKE '_' + nchar(65533) + '_'có hiệu lực. LENso sánh mang lại kết quả khác nhau, do đó, có lẽ chỉ một số hàm chuỗi nhất định.

Tôi nghĩ rằng LIKEhành vi là chính xác cho trường hợp này; nó hoạt động như một giá trị không xác định (có thể là bất cứ thứ gì). Nó cũng xảy ra với những nhân vật khác:

  • nchar(11217) (Dấu hiệu không chắc chắn)
  • nchar(65532) (Nhân vật thay thế đối tượng)
  • nchar(65533) (Nhân vật thay thế)
  • nchar(65534) (Không phải là một nhân vật)

Vì vậy, nếu tôi muốn tìm tất cả các ký tự đại diện cho sự không chắc chắn bằng dấu bằng, tôi sẽ sử dụng một đối chiếu hỗ trợ các ký tự bổ sung như Latin1_General_100_CI_AS_SC.

Tôi đoán đây là nhóm "nhân vật không có trọng lượng" được đề cập trong tài liệu, Collation và Hỗ trợ Unicode .

Câu trả lời:


8

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ả NVARCHARdữ liệu và cho VARCHARdữ 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 VARCHARdữ 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à:

  1. Thứ tự / trọng lượng mặc định được đưa ra trong allkeys.txttệp (ghi chú bên dưới)
  2. 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?)
  3. Bất kỳ ghi đè dựa trên miền địa phương.
  4. Phiên bản của tiêu chuẩn Unicode đang được sử dụng.
  5. 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 100loạ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 VARCHARdữ 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\.0002trả 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 COLLATEmệ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_AICollation):

�
Ԩ
ԩ
Ԫ

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à 0F12và 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 30trọ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 0x0101010100trả 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_WSCollation), 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ó 0x00khóa sắp xếp nào được trả về bởi CompareInfo.GetSortKeybtw) 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 ).


Cảm ơn - trong allkeys.txt được liên kết, có vẻ như không có gì khác có cùng trọng số như FFFD(tìm kiếm *0F12.0020.0002.FFFDchỉ 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.
Martin Smith

1
@MartinSmith Đã thực hiện một số nghiên cứu sử dụng ICU Collation Demo , và đưa vào � A a \u24D0và một vài nghiên cứu khác có trong kết quả phù hợp với 5839. Có vẻ như bạn không thể bỏ qua trọng lượng đầu tiên, và char thay thế này là người duy nhất bắt đầu với 0F12. Nhiều người khác cũng có trọng lượng đầu tiên duy nhất và nhiều người đã bị thiếu hoàn toàn trong tệp allkey. Vì vậy, đây có thể là một lỗi thực hiện do lỗi của con người. Tôi đã thấy char này trong nhóm "không được hỗ trợ" trên trang Unicode trong biểu đồ Collations của họ. Sẽ nhìn nhiều hơn vào ngày mai.
Solomon Rutzky

Rextester sử dụng 4.5. Tôi thực sự thấy ít trận đấu hơn trên phiên bản đó (3385). Có lẽ tôi đang thiết lập một số tùy chọn khác nhau cho bạn? rextester.com/JBWIN31407
Martin Smith

BTW mà khóa sắp xếp 01 01 01 01 00được đề cập ở đây lưu CompareInfo.InternalGetSortKeyLCMapStringEx
Martin Smith

@MartinSmith Tôi đã chơi với nó một chút nhưng không chắc sự khác biệt là gì. Hệ điều hành mà .NET đang chạy không có yếu tố nào. Tôi sẽ tìm kiếm nhiều hơn vào ngày mai nếu có thời gian. Tuy nhiên, bất kể số lượng trận đấu là bao nhiêu, điều này ít nhất dường như xác nhận lý do cho hành vi, đặc biệt là bây giờ chúng tôi có một số thông tin chi tiết về cấu trúc khóa sắp xếp nhờ vào blog bạn liên kết và một số người khác liên kết trong đó. Trang CharUnicodeInfo tôi đã liên kết để đề cập đến các cuộc gọi Collation cơ bản, là cơ sở cho đề xuất của tôi ở đây: connect.microsoft.com/QueryServer/feedback/details/2932336 :-)
Solomon Rutzky
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.