web-dev-qa-db-ja.com

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

_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
Ƕ
Ƿ
Ǹ
_

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

_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_BIN_を使用すると、単一の文字NCHAR(65533)に一致します-しかし、問題は、それが他のケースで使用するルールを理解することです。 _=_に一致する21,229文字の何が特別で、なぜすべてがワイルドカードに一致するのですか?その背後には私が行方不明になっているいくつかの理由があると思います。

nchar(65534) [およびその他の21k]はnchar(65533)と同様に機能します。質問は、nchar(502)を使用して__と同じように表現できます。_LIKE N'%Ƕ%'_(すべてに一致)と_=_の両方の場合と同じように動作します。それはおそらくかなり大きな手がかりです。

最後のクエリのSELECTSELECT 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'Ԛ'_ この範囲外です。

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

この場合、LIKEの動作は正しいと思います。不明な値(何でもかまいません)のように動作します。それはこれらの他のキャラクターにも起こります:

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

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

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

24
Martin Smith

1つの "文字"(複数のコードポイントで構成できる:サロゲートペア、結合文字など)と別の "文字"との比較は、かなり複雑なルールのセットに基づいています。 nicode 仕様で表されているすべての言語に見られるさまざまな(場合によっては「奇抜な」)ルールをすべて考慮する必要があるため、これは非常に複雑です。このシステムは、すべてのNVARCHARデータ、およびSQL Server照合順序(SQL_で始まるもの)ではなくWindows照合順序を使用しているVARCHARデータの非バイナリ照合順序に適用されます。 SQL Server照合順序を使用するVARCHARデータは単純なマッピングを使用するため、このシステムは適用されません。

ほとんどのルールは nicode 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

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

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

Collation elements marked with a "*" are variable.

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

私は、どのパスが取られるかについて完全な調査を行い、より確実な証明を与えることができるようにどのオプションが使用されているかを推測する時間はありませんが、各コードポイント仕様内で、何かにかかわらず「等しい」と見なされると、常に完全な仕様が使用されるとは限りません。この場合、「0F12.0020.0002.FFFD」があり、おそらくレベル2と3だけが使用されています(つまり、。0020.0002。)。 Notepad ++で「.0020.0002」の「カウント」を行う。 12,581件の一致を検索します(まだ処理していない補助文字を含みます)。 「[*」で「カウント」を実行すると、4049件の一致が返されます。 \[\*\d{4}\.0020\.0002のパターンを使用して正規表現「検索」/「カウント」を実行すると、832件の一致が返されます。したがって、この組み合わせのどこかに加えて、おそらく私が表示していない他のいくつかのルールに加えて、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://unicode.org/cldr/utility/character.jsp?a=fffd で確認し、レベル1のソートキー(つまり、FF FD)は「uca」プロパティに一致します。その「uca」プロパティをクリックすると、検索ページに移動します– http://unicode.org/cldr/utility/list-unicodeset.jsp?a=%5B%3Auca%3DFFFD%3A%5D = –一致が1つだけ表示されます。また、allkeys.txtファイルでは、レベル1のソートの重みは0F12として表示され、一致するのは1つだけです。

動作を正しく解釈していることを確認するために、別の文字を調べました。ギリシャ語の大文字のオミクロンVARIA付き at http://unicode.org/cldr/utility/character.jsp?a = 1FF85F30の「uca」(つまり、レベル1のソートウェイト/照合要素)があります。その「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が見つかります。 12E0.のNotepad ++で「カウント」を実行すると、30件の一致が表示されます(これはUnicode.orgの結果と一致しますが、ファイルはUnicode v 5.0用であり、サイトはUnicode v 9.0データを使用しているため、保証されません) 。

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ページに戻り、[Input]ボックスの文字を、SQL Serverの20件の結果のリストから取得した次の3文字に置き換えます。 :

Ὂ
????
Ὸ

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

そのため、この特定の文字は他の文字と一致しないように見えます。

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

SQL Server内とは異なり、.NETには CompareInfo.GetSortKey Method を介して文字列のソートキーを表示する手段があります。このメソッドを使用してU + FFFD文字のみを渡すと、0x0101010100のソートキーが返されます。次に、0〜65535の範囲のすべての文字を繰り返し処理して、0x0101010100の並べ替えキーを持つ文字を確認すると、4529件の一致が返されました。これはSQL Serverで返される5840(Latin1_General_100_CS_AS_WS Collat​​ionを使用する場合)と正確に一致しませんが、Windows 10および.NET Frameworkバージョン4.6.1を実行していることを考えると、現時点では(現時点で)取得できる最も近いものです CharUnicodeInfo Class のグラフに従って、Unicode v 6.3.0を使用します(「備考」セクションの「呼び出し元への注意」内)。現時点では、SQLCLR関数を使用しているため、ターゲットフレームワークのバージョンを変更できません。機会があったら、コンソールアプリを作成し、ターゲットフレームワークバージョン4.5を使用します。これは、100シリーズの照合順序に一致するUnicode v 5.0を使用するためです。

このテストが示すのは、U + FFFDの.NETとSQL Serverの間で正確に同じ数の一致がなくても、これがSQL Server固有ではないであることは明らかです動作、およびMicrosoftの実装による意図的なものか見落としかにかかわらず、U + FFFD文字は、Unicode仕様に従っていないはずの場合でも、実際にはかなりの数の文字と一致します。また、この文字がU + 0000(null)と一致する場合、重みが欠落しているだけの問題である可能性があります。

[〜#〜]また[〜#〜]

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

12
Solomon Rutzky