なぜ数字以外が「0-9」なのですか?


13

私のサーバーのデフォルトの照合は、次のクエリによって決定されるLatin1_General_CI_ASです。

SELECT SERVERPROPERTY('Collation') AS Collation;

この照合により、述語を使用して文字列内の数字以外の文字と一致できることを発見して驚きましたLIKE '[0-9]'

デフォルトの照合でこれが起こるのはなぜですか?これが役立つケースは考えられません。バイナリ照合を使用して動作を回避できることはわかっていますが、デフォルトの照合を実装する奇妙な方法のようです。

数字をフィルタリングすると、数字以外の文字が生成されます

すべての可能なシングルバイト文字値を含む列を作成し、数字一致述語で値をフィルタリングすることにより、動作を実証できます。

次のステートメントは、現在のコードページの各コードポイントに1つずつ、256行の一時テーブルを作成します。

WITH P0(_) AS (SELECT 0 UNION ALL SELECT 0),
P1(_) AS (SELECT 0 FROM P0 AS L CROSS JOIN P0 AS R),
P2(_) AS (SELECT 0 FROM P1 AS L CROSS JOIN P1 AS R),
P3(_) AS (SELECT 0 FROM P2 AS L CROSS JOIN P2 AS R),
Tally(Number) AS (
  SELECT -1 + ROW_NUMBER() OVER (ORDER BY (SELECT 0))
  FROM P3
)
SELECT Number AS CodePoint, CHAR(Number) AS Symbol
INTO #CodePage
FROM Tally
WHERE Number >= 0 AND Number <= 255;

各行には、コードポイントの整数値とコードポイントの文字値が含まれています。すべての文字値が表示可能というわけではありません-一部のコードポイントは厳密に制御文字です。次に、出力の選択サンプルを示しますSELECT CodePoint, Symbol FROM #CodePage

0   
1   
2   
...
32   
33  !
34  "
35  #
...
48  0
49  1
50  2
...
65  A
66  B
67  C
...
253 ý
254 þ
255 ÿ

LIKE述語を使用し、「0」から「9」までの文字の範囲を指定して、Symbol列でフィルタリングして数字を見つけることができると期待しています。

SELECT CodePoint, Symbol
FROM #CodePage
WHERE Symbol LIKE '[0-9]';

それは驚くべき出力を生成します:

CodePoint   Symbol
48  0
49  1
50  2
51  3
52  4
53  5
54  6
55  7
56  8
57  9
178 ²
179 ³
185 ¹
188 ¼
189 ½
190 ¾

48から57までのコードポイントのセットは、私が期待するものです。私が驚いたのは、上付き文字と分数の記号も結果セットに含まれていることです!

指数と分数を数字と考える数学的理由があるかもしれませんが、それらを数字と呼ぶのは間違っているようです。

回避策としてバイナリ照合を使用する

期待どおりの結果を得るために、対応するバイナリ照合Latin1_General_BINを強制できることを理解しています。

SELECT CodePoint, Symbol
FROM #CodePage
WHERE Symbol LIKE '[0-9]' COLLATE Latin1_General_BIN;

結果セットには、48〜57のコードポイントのみが含まれます。

CodePoint   Symbol
48  0
49  1
50  2
51  3
52  4
53  5
54  6
55  7
56  8
57  9

回答:


22

[0-9] 数字に一致するように定義された正規表現ではありません。

LIKEパターンの任意の範囲は、照合ソート順に従って開始文字と終了文字の間の文字と一致します。

SELECT CodePoint,
       Symbol,
       RANK() OVER (ORDER BY Symbol COLLATE Latin1_General_CI_AS) AS Rnk
FROM   #CodePage
WHERE  Symbol LIKE '[0-9]' COLLATE Latin1_General_CI_AS
ORDER  BY Symbol COLLATE Latin1_General_CI_AS 

戻り値

CodePoint            Symbol Rnk
-------------------- ------ --------------------
48                   0      1
188                  ¼      2
189                  ½      3
190                  ¾      4
185                  ¹      5
49                   1      5
50                   2      7
178                  ²      7
179                  ³      9
51                   3      9
52                   4      11
53                   5      12
54                   6      13
55                   7      14
56                   8      15
57                   9      16

デフォルトの照合では、これらの文字は後0だが前にソートされるため、これらの結果が得られます9

照合は、0との間の正しい順序の分数で実際にそれらを数学的な順序でソートするように定義されているように見え1ます。

範囲ではなくセットを使用することもできます。照合を回避2する²には、CS照合が必要になります

SELECT CodePoint, Symbol
FROM #CodePage
WHERE Symbol LIKE '[0123456789]' COLLATE Latin1_General_CS_AS

6

Latin1はコードページ1252で、178は 'SUPERSCRIPT TWO'です。これは、Unicodeで上付き文字:で上付き文字として「2」Unicode Technical Standard#10によるとに 2に相当するはずです。8.1照合折りたたみを参照してください。

マップ互換性(3次)同等物、 全角文字や上付き文字などのを代表文字にマッピングします

バグは、上付き文字2が2とは異なる場合です。「私のコラムはユニコードではありません」と言う前に、安心してください:MSDN(Windows Collat​​ionsを参照)によると、ディスク上の表現がCHARであっても、すべての文字列の比較とソートはユニコードのルールに従って行われます。

あなたの例の他のキャラクターについては、 VULGAR FRACTION ONE QUARTERなどは数字と比較されませんが、マークがすでに示したように、0〜9の間で適切にソートされます。

そして、もちろん、コードページを変更すると、異なる結果が得られます。例えば。Greek_CS_ASコードページ1253)あなたは、コード178、179および189で文字になるだろう。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.