LIKE N '% %'の検索がUnicode文字に一致し、=N' 'が多くに一致するのはなぜですか?


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

返品

Col
A
B
C
Ƕ
Ƿ
Ǹ

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

返品

Col
Ƕ
Ƿ
Ǹ

以下ですべての可能なダブルバイト「文字」を生成=すると、LIKE N'%�%'バージョンがそれらの21,229に一致し、すべてのバージョンに一致することがわかります(同じ結果でいくつかの非バイナリ照合を試しました)。

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

ここで何が起こっているのかについて光を当てることができる人はいますか?

COLLATE Latin1_General_BINthen を使用すると、単一の文字に一致しますNCHAR(65533)が、質問は、他の場合にどの規則を使用するかを理解することです。に一致するそれらの21,229文字の特別な点=と、すべてがワイルドカードに一致するのはなぜですか?その背後には、私が行方不明になっている何らかの理由があると思います。

nchar(65534)[および21kその他]は同様に機能しnchar(65533)ます。質問は次のようにnchar(502) 等しく表現できます- LIKE N'%Ƕ%'(すべてに一致する)と=ケースの両方で同じように動作します。それはおそらく非常に大きな手がかりです。

SELECT最後のクエリでを変更すると、SELECT I, N, RANK() OVER(ORDER BY N)SQL Serverが文字をランク付けできないことが示されます。照合で処理されない文字は同等と見なされるようです。

Latin1_General_100_CS_AS照合のあるデータベースは、5840の一致を生成します。マッチをかなりLatin1_General_100_CS_AS削減し=ますが、LIKE動作を変更しません。後の照合では文字数が少なくなり、すべてが同等に比較され、ワイルドカードLIKE検索では無視されるように見えます。

私はSQL Server 2016を使用しています。シンボルはUnicode置換文字ですが、UCS-2エンコーディングで無効な文字は55296-57343 AFAIKのみでありN'Ԛ'、この範囲にないような完全に有効なコードポイントと明確に一致しています。

これらの文字はすべて、LIKEand の空の文字列のように動作します=。同等と評価することさえできます。N'' = N'�'真でありLIKE、単一のスペースの比較でそれをドロップしLIKE '_' + nchar(65533) + '_'ても効果はありません。LENただし、比較の結果は異なるため、おそらく特定の文字列関数にすぎません。

LIKEこの場合の動作は正しいと思います。それは未知の値のように振る舞います(これは何でも可能です)。これらの他のキャラクターにも起こります:

  • nchar(11217) (不確実性記号)
  • nchar(65532) (オブジェクト置換文字)
  • nchar(65533) (交換キャラクター)
  • nchar(65534) (キャラクターではない)

したがって、等号で不確実性を表すすべての文字を検索する場合は、などの補助文字をサポートする照合を使用しますLatin1_General_100_CI_AS_SC

これらは、ドキュメント、照合およびユニコードのサポートで言及されている「重みのない文字」のグループだと思います。

回答:


8

1つの「キャラクター」(複数のコードポイントで構成されることがあります:サロゲートペア、キャラクターの組み合わせなど)が他の「キャラクター」と比較される方法は、かなり複雑なルールセットに基づいています。Unicode仕様で表されているすべての言語に見られるさまざまな(そして時々「奇抜な」)ルールすべてを考慮する必要があるため、非常に複雑です。このシステムは、すべてのNVARCHARデータ、およびVARCHARSQL Server照合ではなくWindows照合を使用しているデータ(1で始まるものSQL_)の非バイナリ照合に適用されます。このシステムはVARCHAR、単純なマッピングを使用するため、SQL Server Collat​​ionを使用するデータには適用されません。

ほとんどのルールは、Unicode Collat​​ion Algorithm(UCA)で定義されています。これらのルールの一部と、UCAでカバーされていないルールは次のとおりです。

  1. allkeys.txtファイルで指定されているデフォルトの順序/重み(以下に記載)
  2. どの感度とオプションが使用されているか(たとえば、大文字と小文字を区別するのか、区別しないのか、大文字と小文字を区別する場合は、大文字が最初か小文字が最初か)
  3. ロケールベースのオーバーライド。
  4. Unicode標準のバージョンが使用されています。
  5. 「人間」要素(つまり、Unicodeはソフトウェアではなく仕様であるため、各ベンダーが実装する必要があります)

ヒューマンファクターに関する最後のポイントは、SQL Serverが仕様に従って常に100%動作することを期待してはならないことを明確にすることです。

ここでの最大の要因は、各コードポイントに与えられる重みと、複数のコードポイントが同じ重み仕様を共有できるという事実です。基本的な重み(ロケール固有のオーバーライドはありません)は、ここで見つけることができます(100一連の照合順序はUnicode v 5.0であると信じています-Microsoft Connectアイテムのコメントでの非公式の確認)。

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

問題のコードポイント– U + FFFD –は次のように定義されます。

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

その表記法は、セクション9.1 UCAのAllkeysファイル形式で定義されています

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

Collation elements marked with a "*" are variable.

この最後の行は重要です。コードポイントには実際に「*」で始まる仕様があるためです。セクション3.6 Variable Weightingには、直接アクセスできない照合設定値に基づいて4つの可能な動作が定義されています(これらは、大文字と小文字を区別するかどうかなど、Microsoftの各照合の実装にハードコードされています最初に大文字、照合をVARCHAR使用するデータSQL_と他のすべてのバリエーションで異なるプロパティ)。

より確実な証明を提供できるように、どのパスが使用されるかを完全に調査し、どのオプションが使用されているかを推測する時間はありませんが、各コードポイント仕様内で、何かにかかわらず「等しい」と見なされると、常に完全な仕様が使用されるとは限りません。この場合、「0F12.0020.0002.FFFD」があり、ほとんどの場合、使用されているのはレベル2と3だけです(つまり.0020.0002。)。「.0020.0002」のメモ帳++で「カウント」を行う 12,581件の一致を検出します(まだ処理していない補助文字を含む)。「[*」で「Count」を実行すると、4049件の一致が返されます。次のパターンを使用して正規表現「検索」/「カウント」を行う\[\*\d{4}\.0020\.0002832件の一致を返します。そのため、この組み合わせのどこかと、おそらく私が見ない他のいくつかのルールと、Microsoft固有の実装の詳細が、この動作の完全な説明です。明確にするために、ルールが適用されるとすべて同じ重みを持つため、一致するすべての文字の動作は同じです(つまり、この質問は、それらのいずれかについて尋ねられた可能性があります必ず氏)。

COLLATE以下のクエリを使用して、2つのバージョンの照合でさまざまな感度がどのように機能するかをクエリの下の結果に従って句を変更することで確認できます。

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

異なる照合で一致する文字のさまざまなカウントは以下のとおりです。

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

上記のすべての照合では、N'' = N'�'trueと評価されます。

更新

私はもう少し研究をすることができましたが、ここに私が見つけたものがあります:

「おそらく」動作する方法

ICU Collat​​ion Demoを使用して、ロケールを "en-US-u-va-posix"に設定し、強度を "primary"に設定し、show "sort keys"をオンにし、次の4文字をコピーしました。上記のクエリの結果(Latin1_General_100_CI_AI照合を使用):

�
Ԩ
ԩ
Ԫ

そしてそれは返します:

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

次に、http:FF FD //unicode.org/cldr/utility/character.jsp?a = fffdで文字プロパティの「 」を確認し、レベル1のソートキー(つまり)が「uca」プロパティと一致することを確認します。-その「UCA」プロパティをクリックすると、検索ページに移動しますhttp://unicode.org/cldr/utility/list-unicodeset.jsp?a=%5B%3Auca%3DFFFD%3A%5D -わずか1試合を示します。また、allkeys.txtファイルでは、レベル1のソートの重みはとして表示され0F12、それに一致するものは1つだけです。

GREEK CAPITAL LETTER OMICRON WITH VARIA:私たちは正しく行動を解釈していることを確認するために、私は別の文字を見http://unicode.org/cldr/utility/character.jsp?a=1FF8(「UCA」を持っていますすなわち、レベル1のソート重み/照合要素)の5F30。-その「5F30」をクリックすると、検索ページに移動しますhttp://unicode.org/cldr/utility/list-unicodeset.jsp?a=%5B%3Auca%3D5F30%3A%5D - 30試合の20を示します0〜65535の範囲(つまり、U + 0000-U + FFFF)にあります。探しallkeys.txtコードポイント用のファイル1FF8、我々はレベル1ソートの重みを参照してください12E0。Notepad ++で「カウント」を行う12E0. 30個の一致を示します(Unicode v 5.0のファイルであり、サイトはUnicode v 9.0のデータを使用しているため保証されませんが、Unicode.orgの結果と一致します)。

SQL Serverでは、次のクエリは20個の一致を返します。10個の補助文字を削除するときのUnicode.org検索と同じです。

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

そして、念のため、ICU Collat​​ion Demoページに戻り、「入力」ボックスの文字を、SQL Serverの20個の結果のリストから取得した次の3文字に置き換えます。


𝜪

実際、これらはすべて同じ5F 30レベル1のソートウェイトを持っていることを示しています(キャラクタープロパティページの「uca」フィールドに一致)。

SO、それは確かに、この特定の文字をすべきかのように思われるんではない何かを一致させます。

実際の動作(少なくともMicrosoftランドでは)

SQL Server内とは異なり、.NETにはCompareInfo.GetSortKey Methodを介して文字列のソートキーを表示する手段があります。このメソッドを使用して、U + FFFD文字だけを渡すと、ソートキーが返されます0x0101010100。次に、0〜65535の範囲のすべての文字を反復処理して、0x01010101004529個の一致の返される並べ替えキーがあった文字を確認します。これは、SQL Serverで返された5840(Latin1_General_100_CS_AS_WS照合を使用する場合)と完全には一致しませんが、Unicode vを使用するWindows 10および.NET Frameworkバージョン4.6.1を実行しているため、(現時点では)取得できる最も近いものです6.3.0 CharUnicodeInfoクラスのチャートによる(「発言者」の「備考」セクション)。現時点では、SQLCLR関数を使用しているため、ターゲットフレームワークのバージョンを変更できません。機会があれば、コンソールアプリを作成し、Unicode v 5.0を使用する4.5のターゲットフレームワークバージョンを使用します。これは、100シリーズの照合に一致する必要があります。

このテストが示すのは、.NETとU + FFFDのSQL Serverでまったく同じ数の一致がなくても、これがSQL Server固有の動作ではなく、実装が意図的であるか監視されているかは明らかです。Microsoftによると、U + FFFD文字は、Unicode仕様に従っていなくても、実際にかなりの数の文字と一致します。そして、この文字がU + 0000(null)に一致することを考えると、おそらく重みの欠落の問題にすぎません。

また

=クエリとクエリの動作の違いについてはLIKE N'%�%'、ワイルドカードと、これらの(つまり� Ƕ Ƿ Ǹ)文字の欠落している(と思われる)重みに関係しています。LIKE条件が単純に変更されると、条件とLIKE N'�'同じ3行が返され=ます。ワイルドカードの問題が「欠落」ウェイトによるものではない場合(btw 0x00によってソートキーが返されないCompareInfo.GetSortKey場合)、コンテキストに基づいてソートキーを変更できるプロパティを持つ可能性があるこれらの文字(つまり、周囲の文字) )。


ありがとう-リンクされたallkeys.txtでは、同じ重みを与えられたものは他にないように見えますFFFD(検索*0F12.0020.0002.FFFDは1つの結果のみを返します)。@Forrestの観察では、これらはすべて空の文字列に一致し、件名についてもう少し読むと、さまざまな非バイナリ照合で共有する重みは実際にはゼロであるように見えます。
マーティンスミス

1
@MartinSmithは、使用していくつかの研究でしたICU照合デモをし、中に入れ� A a \u24D0そして5839の試合にあったいくつかの他のセットを引き起こします。最初のウェイトをスキップできないように見えます。この置換文字はで始まる唯一の文字です0F12。他にも多くの人が一意の最初の重みを持ち、多くはallkeysファイルから完全に欠落していました。したがって、これは人為的ミスによる実装のバグである可能性があります。Unicode文字サイトの「サポートされていない」グループの照合チャートでこの文字を確認しました。明日はもっと見えるでしょう。
ソロモンラッツキー

Rextesterは4.5を使用します。実際、そのバージョン(3385)で一致するものは少なくなっています。たぶんあなたとは異なるオプションを設定していますか?rextester.com/JBWIN31407
マーティンスミス

ところで、そのソートキー01 01 01 01 00はここに記載されていますarchives.miloush.net/michkap/archive/2007/09/10/4847780.htmlCompareInfo.InternalGetSortKey呼び出しのように見えますLCMapStringEx
マーティンスミス

@MartinSmith少し遊んでみましたが、違いはまだわかりません。.NETが実行されているOSは考慮に入れられます。時間があれば、明日もっと調べます。一致の数に関係なく、これは少なくとも動作の理由を確認しているように見えます。特に、リンク先のブログとそれにリンクしている他のブログのおかげで、ソートキー構造についての洞察が得られているためです。:私がリンクされCharUnicodeInfoページはここに私の提案の基礎となる基礎となる照合呼び出し、言及connect.microsoft.com/SQLServer/feedback/details/2932336 :-)
ソロモンRutzky
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.