Neden LIKE N '% %' için arama herhangi bir Unicode karakteriyle ve = N' 'ile eşleşiyorsa çok fazla?


21
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'%�%'

İade

Col
A
B
C
Ƕ
Ƿ
Ǹ

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

İade

Col
Ƕ
Ƿ
Ǹ

Aşağıdaki her olası çift bayt "karakteri" oluşturmak, =sürümün 21.229 tanesi ve LIKE N'%�%'hepsinin sürümüyle eşleştiğini gösterir (Aynı sonuçla birkaç ikili olmayan harmanlamayı denedim).

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'�'  

Burada neler olduğuna dair herhangi bir ışık tutabilecek olan var mı?

Daha COLLATE Latin1_General_BINsonra kullanmak tek karakterle eşleşir NCHAR(65533)- ancak soru, diğer durumda hangi kuralları kullandığını anlamaktır. 21.229 karakterle eşleşen özel karakter nedir =ve neden her şey joker karakterle eşleşir? Arkasında kayıp olmamın bir nedeni olduğunu düşünüyorum.

nchar(65534)[ve 21 bin kişi daha] aynı şekilde çalışıyor nchar(65533). Soru nchar(502) eşit olarak ifade edilebilirdi - hem LIKE N'%Ƕ%'(her şeyle eşleşir) hem de durumda olduğu gibi davranır =. Muhtemelen büyük bir ipucu.

Değişen SELECTson sorguda SELECT I, N, RANK() OVER(ORDER BY N)gösterileri SQL Server karakterleri rütbe olamaz. Harmanlama tarafından ele alınmayan herhangi bir karakterin eşdeğer kabul edildiği görülmektedir.

Latin1_General_100_CS_ASHarmanlama içeren bir veritabanı 5840 eşleşme üretir. maçları oldukça önemli ölçüde Latin1_General_100_CS_ASazaltır =, ancak LIKEdavranışı değiştirmez . Daha sonraki harmanlamalarda daha küçük olan ve hepsi eşit olan ve LIKEo zaman joker karakter aramalarında göz ardı edilen bir karakter potu var gibi görünüyor .

SQL Server 2016 kullanıyorum. Sembol Unicode değiştirme karakteri, ancak UCS-2 kodlamasındaki tek geçersiz karakterler 55296 - 57343 AFAIK ve N'Ԛ'bu aralıkta olmayanlar gibi mükemmel geçerli kod noktalarıyla eşleşiyor .

Tüm bu karakterler LIKEve için boş bir dize gibi davranır =. Hatta eşdeğer olarak değerlendirilirler. N'' = N'�'doğrudur ve LIKEtek bir boşluğun LIKE '_' + nchar(65533) + '_'hiçbir etkisi olmadan karşılaştırmasına bırakabilirsiniz . LENkarşılaştırmalar farklı sonuçlar verir, bu yüzden muhtemelen sadece belirli dize işlevleri.

LIKEDavranışın bu durum için doğru olduğunu düşünüyorum ; bilinmeyen bir değer gibi davranır (herhangi bir şey olabilir). Bu diğer karakterler için de olur:

  • nchar(11217) (Belirsizlik İşareti)
  • nchar(65532) (Nesne Değiştirme Karakteri)
  • nchar(65533) (Yedek Karakter)
  • nchar(65534) (Bir Karakter Değil)

Eşit işaret ile belirsizliği temsil eden tüm karakterleri bulmak istersem, ek karakterleri destekleyen bir harmanlama kullanırdım Latin1_General_100_CI_AS_SC.

Bunlar, dokümantasyon, Harmanlama ve Unicode Desteği'nde belirtilen "ağırlıklı olmayan karakterler" grubudur .

Yanıtlar:


9

Bir "karakterin" (birden çok Kod Noktasından oluşabilir: vekil çiftleri, karakterleri birleştirme vb.) Diğeriyle nasıl karşılaştırıldığı oldukça karmaşık bir kurallar kümesine dayanır. Unicode belirtiminde temsil edilen tüm dillerde bulunan çeşitli (ve bazen "tuhaf") kuralların dikkate alınması gerektiği için çok karmaşıktır . Bu sistem, tüm NVARCHARveriler ve VARCHARbir Windows Server Harmanlama kullanan ve bir SQL Server Harmanlama (bir tanesi ile başlayan SQL_) kullanan veriler için ikili olmayan Harmanlamalara uygulanır . Bu sistem, VARCHARbasit eşlemeler kullandığından SQL Server Harmanlama kullanan veriler için geçerli değildir .

Kuralların çoğu Unicode Harmanlama Algoritması'nda (UCA) tanımlanmıştır . Bu kurallardan bazıları ve bazıları UCA'da olmayanlar:

  1. allkeys.txtDosyada verilen varsayılan sipariş / ağırlık (aşağıda belirtilmiştir)
  2. Hangi hassasiyetler ve seçenekler kullanılıyor (örn. Büyük / küçük harfe duyarlı mı yoksa duyarsız mı?) Ve eğer duyarlıysa, büyük harf birinci mi yoksa küçük harf mi?)
  3. Yerel ayar tabanlı geçersiz kılmalar.
  4. Unicode standardının sürümü kullanılıyor.
  5. "İnsan" faktörü (yani Unicode yazılım değil, bir özelliktir ve bu nedenle onu uygulamak için her satıcıya bırakılmıştır)

İnsan faktörü ile ilgili son noktanın, SQL Server'ın spesifikasyona göre her zaman% 100 davranmasını beklememesi gerektiğini açıkça belirtmek için vurguladım.

Buradaki geçersiz kılma faktörü, her bir Kod Noktasına verilen ağırlıktır ve birden fazla Kod Noktasının aynı ağırlık spesifikasyonunu paylaşabilmesidir. Burada temel ağırlıkları bulabilirsiniz (bölgeye özgü geçersiz kılmalar yok) ( 100Harmanlama serisinin Unicode v 5.0 - Microsoft Connect öğesindeki yorumlarda gayri resmi onay olduğuna inanıyorum ):

http://www.unicode.org/Public/UCA/5.0.0/allkeys.txt

Söz konusu Kod Noktası - U + FFFD - şöyle tanımlanır:

FFFD  ; [*0F12.0020.0002.FFFD] # REPLACEMENT CHARACTER

Bu gösterim, UCA'nın 9.1 Allkeys Dosya Biçimi bölümünde tanımlanmıştır :

<entry>       := <charList> ';' <collElement>+ <eol>
<charList>    := <char>+
<collElement> := "[" <alt> <weight> "." <weight> "." <weight> ("." <weight>)? "]"
<alt>         := "*" | "."

Collation elements marked with a "*" are variable.

Baktığımız Kod Noktası "*" ile başlayan bir spesifikasyona sahip olduğundan, bu son satır önemlidir. 3.6 Değişken Ağırlıklandırma bölümünde , doğrudan erişimimiz olmayan Harmanlama yapılandırması değerlerine bağlı olarak tanımlanan dört olası davranış vardır (bunlar her Harmanlamanın Microsoft uygulamasına sabit olarak kodlanmıştır; örneğin, büyük / küçük harfe duyarlı veya küçük harf kullanıyorsa veya büyük harf ilk olarak, Harmanlamaları VARCHARkullanan veriler SQL_ve diğer tüm varyasyonlar arasında farklı olan bir özellik ).

Yolların alındığı tam araştırmayı yapmak ve daha sağlam bir kanıt verilebilecek şekilde hangi seçeneklerin kullanıldığını çıkarmak için zamanım yok, ancak her Kod Noktası belirtiminde bir şey olsun veya olmasın söylemek güvenli "eşit" kabul edilir her zaman tam şartname kullanmayacak. Bu durumda, "0F12.0020.0002.FFFD" var ve büyük olasılıkla kullanılan seviye 2 ve 3'tür (yani .0020.0002 ). ".0020.0002" için Notepad ++ uygulamasında bir "Sayma" işlemi yapıyor. 12.581 sonuç bulundu (henüz ele almadığımız ek karakterler dahil). "[*" Üzerinde "Say" işlemi yapmak 4049 eşleşme döndürür. Bir desen kullanarak RegEx "Bul" / "Sayımı" yapmak\[\*\d{4}\.0020\.0002832 eşleşme döndürür. Yani bu kombinasyonda bir yerde, artı göremediğim bazı diğer kurallar ve ayrıca Microsoft'a özgü bazı uygulama ayrıntıları, bu davranışın tam açıklamasıdır. Ve açık olmak gerekirse, davranış, eşleşen tüm karakterler için aynıdır, çünkü kurallar uygulandıktan sonra hepsi aynı ağırlığa sahiptirler (yani, bu soru bunlardan herhangi biri hakkında sorulabilirdi, değil mutlaka Bay ).

Aşağıdaki sorguyu görebilir ve sorgunun altındaki COLLATEsonuçlara göre cümleleri değiştirerek , çeşitli duyarlılıkların Harmanlamaların iki sürümünde nasıl çalıştığını görebilirsiniz:

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

Farklı harmanlamalarda eşleşen karakterlerin çeşitli sayıları aşağıdadır.

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

Yukarıda listelenen tüm harmanlamalarda N'' = N'�'da doğru olarak değerlendirilir.

GÜNCELLEME

Biraz daha araştırma yapabildim ve işte bulduğum şey:

"Muhtemelen" nasıl çalışmalı

YBÜ Harmanlama Demosunu kullanarak, yerel ayarı "en-US-u-va-posix" olarak ayarladım, gücü "birincil" olarak ayarladım, işaretli "sıralama tuşlarını" kontrol ettim ve kopyaladığım aşağıdaki 4 karaktere yapıştırdım. yukarıdaki sorgunun sonuçları ( Latin1_General_100_CI_AIHarmanlama kullanılarak):

�
Ԩ
ԩ
Ԫ

ve bu döndürür:

Ԫ
    60 2E 02 .
Ԩ
    60 7A .
ԩ
    60 7A .
�
    FF FD .

Ardından, http://unicode.org/cldr/utility/character.jsp?a=fffd adresinden " " için karakter özelliklerini kontrol edin ve düzey 1 sıralama anahtarının (yani FF FD) "uca" özelliğiyle eşleştiğini görün. Bu "uca" mülkünü tıkladığınızda, yalnızca 1 eşleşmeyi gösteren bir arama sayfasına ( http://unicode.org/cldr/utility/list-unicodeset.jsp?a=%5B%3Auca%3DFFFD%3A%5D) yönlendirilirsiniz . Ve allkeys.txt dosyasında, seviye 1 sıralama ağırlığı olarak gösterilir 0F12ve bunun için yalnızca 1 eşleşme vardır.

Biz doğru davranış yorumlama emin olmak için, ben başka bir karakter baktı: YUNAN BÜYÜK HARFLERLE OMICRON İLE varia de http://unicode.org/cldr/utility/character.jsp?a=1FF8 hangi bir "uca" ( yani seviye 1 sıralama ağırlığı / harmanlama elemanı) 5F30. Bu "5F30" u tıklamak bizi bir arama sayfasına götürür - http://unicode.org/cldr/utility/list-unicodeset.jsp?a=%5B%3Auca%3D5F30%3A%5D - 30 eşleşme gösteriliyor, 20 / 0 - 65535 aralığındadır (yani U + 0000 - U + FFFF). Baktığında allkeys.txt Kod Noktası için dosyanın 1FF8 , biz bir seviye 1 sıralama ağırlığını bkz 12E0. Notepad ++ uygulamasında "Sayma" işlemi12E0. 30 eşleşmeyi gösterir (dosya Unicode v 5.0 için olduğu ve site Unicode v 9.0 verilerini kullandığı için garanti edilmese de, bu Unicode.org'un sonuçlarıyla eşleşir).

SQL Server'da, aşağıdaki sorgu 10 ek karakteri kaldırırken Unicode.org aramasıyla aynı olan 20 eşleşmeyi döndürür:

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

Ve emin olmak için, ICU Harmanlama Demosu sayfasına geri dönme ve "Giriş" kutusundaki karakterleri SQL Server'dan 20 sonuç listesinden alınan aşağıdaki 3 karakterle değiştirme:


𝜪

aslında hepsinin aynı 5F 30seviye 1 sıralama ağırlığına sahip olduğunu gösterir (karakter özelliği sayfasındaki "uca" alanı ile eşleşir).

SO, kesinlikle bu özel karakter olmalıdır sanki görünüyor değil başka bir şey uyuyor.

Aslında nasıl çalışır (en azından Microsoft-Land'de)

SQL Server'ın aksine, .NET, CompareInfo.GetSortKey Yöntemi aracılığıyla bir dize için sıralama anahtarını göstermenin bir yoluna sahiptir . Bu yöntemi kullanarak ve sadece U + FFFD karakterini ileterek, bir sıralama anahtarı döndürür 0x0101010100. Daha sonra, 0 - 65535 aralığındaki tüm karakterleri tekrarlayan, hangilerinin 0x01010101004529 eşleşme döndürdüğünü görmek için . Bu, SQL Server'da döndürülen 5840 ile tam olarak eşleşmiyor ( Latin1_General_100_CS_AS_WSHarmanlama kullanılırken), ancak Unicode v kullanan Windows 10 ve .NET Framework sürüm 4.6.1'i çalıştırdığımda (şimdilik) alabileceğimiz en yakın şey 6.3.0 CharUnicodeInfo Sınıfı grafiğine göre("Arayanlara Not", "Notlar" bölümünde). Şu an bir SQLCLR işlevi kullanıyorum ve bu nedenle hedef Framework sürümünü değiştiremiyorum. Bir şans elde ettiğimde, bir konsol uygulaması oluşturacağım ve 100 serisi Harmanlamalarla eşleşmesi gereken Unicode v 5.0'ı kullanan 4.5 hedef Framework sürümünü kullanacağım.

Ne bu test gösterileri bile .NET ve U + FFFD için SQL Server arasında maçların aynı numarası olmadan, yani, bu oldukça açıktır değil SQL Server özgü davranış ve uygulama ile kasıtlı veya gözetim yapılması olduğunu olsun Microsoft tarafından, U + FFFD karakteri, Unicode belirtimine uygun olmasa bile, gerçekten birkaç karakterle eşleşir. Ve bu karakterin U + 0000 (null) ile eşleştiği göz önüne alındığında, muhtemelen sadece ağırlık eksikliğidir.

AYRICA

=Sorgu vs sorgu davranış davranış farkı ile ilgili olarak LIKE N'%�%', bu joker karakterler ve bu (yani � Ƕ Ƿ Ǹ) karakterler için eksik (varsayalım) ağırlıkları ile ilgili . LIKEKoşul basit olarak değiştirilirse , koşulla LIKE N'�'aynı 3 satırı döndürür =. Joker karakterlerle ilgili sorun "eksik" ağırlıklardan kaynaklanmıyorsa ( btw 0x00tarafından döndürülen bir sıralama anahtarı yoktur CompareInfo.GetSortKey), bu karakterlerin potansiyel olarak sıralama anahtarının bağlama göre değişmesine izin veren bir özelliğe sahip olmasından kaynaklanabilir (yani, çevreleyen karakterler) ).


Teşekkürler - allkeys.txt bağlantılı, aynı ağırlık verilen başka bir şey yok gibi görünüyor FFFD(arama *0F12.0020.0002.FFFDsadece bir sonuç döndürür). @ Forrest'in hepsinin boş dizeyle eşleştiği ve konu üzerinde biraz daha fazla okuma yaptığı gözleminden, çeşitli ikili olmayan harmanlamalarda paylaştıkları ağırlığa benziyor aslında aslında sıfır olduğuna inanıyorum.
Martin Smith

1
@MartinSmith kullanarak bazı araştırma yaptım ICU Karşılaştırma Demo ve koyarak � A a \u24D0ve 5839 maçta vardı birkaç başka dizi sonuçlanır. İlk ağırlığı atlayamayacağınız anlaşılıyor ve bu değiştirme charı tek başlangıç 0F12. Birçoğu da benzersiz bir ilk ağırlığa sahipti ve çoğu allkeys dosyasında tamamen eksikti. Bu, insan hatası nedeniyle bir uygulama hatası olabilir. Bu char, Harmanlama listelerinde Unicode sitesinde "desteklenmeyen" grubunda gördüm. Yarın daha fazla görünecek.
Solomon Rutzky

Rextester 4.5 kullanıyor. Aslında bu versiyonda daha az eşleşme görüyorum (3385). Belki sizden farklı bir seçenek ayarlıyorum? rextester.com/JBWIN31407
Martin Smith

BTW bu tür anahtardan 01 01 01 01 00bahsedilir burada arşivler.miloush.net/ CompareInfo.InternalGetSortKeyLCMapStringEx
Martin Smith

@MartinSmith Onunla biraz oynadım ama farkın ne olduğundan emin değilim. .NET'in üzerinde çalıştığı işletim sistemi de etkiliyor. Zamanım varsa yarın daha fazla bakacağım. Eşleşme sayısına bakılmaksızın, bu en azından davranışın nedenini doğrulıyor gibi görünüyor, özellikle de bağlandığınız blog ve buna bağlı bazı diğerleri sayesinde sıralama anahtar yapısı hakkında bazı bilgilerimiz var. Bağlantı verdiğim CharUnicodeInfo sayfası, burada önerim için temel olan temel Harmanlama çağrılarından bahsediyor: connect.microsoft.com/SQLServer/feedback/details/2932336 :-)
Solomon Rutzky 21:17
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.