Почему не-цифры нравятся [0-9]?

13

По умолчанию мой сервер сортировки является Latin1_General_CI_AS, как определено этим запросом:

SELECT SERVERPROPERTY('Collation') AS Collation;

Я был удивлен, обнаружив, что с этим сопоставлением я могу сопоставлять нецифровые символы в строках, используя предикат LIKE '[0-9]'.

Почему в сопоставлении по умолчанию это происходит? Я не могу вспомнить случай, когда это было бы полезно. Я знаю, что могу обойти поведение, используя двоичное сопоставление, но это кажется странным способом реализации сопоставления по умолчанию.

Фильтрация цифр дает нецифровые символы

Я могу продемонстрировать поведение, создав столбец, который содержит все возможные значения однобайтовых символов, и отфильтровав значения с помощью предиката сопоставления цифр.

Следующий оператор создает временную таблицу с 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 ÿ

Я ожидаю, что смогу отфильтровать по столбцу Symbol, чтобы найти цифровые символы, используя предикат LIKE и указав диапазон символов от «0» до «9»:

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 # 10 он должен сравниваться равным 2, см. 8.1 Сворачивание сортировки :

Сопоставить (третичные) совместимые эквиваленты, такие как символы полной ширины и надстрочные символы , с характерными символами

Ошибка будет, если верхний индекс 2 будет отличаться от 2! Прежде чем сказать «но мой столбец не Unicode», будьте уверены: в соответствии с MSDN (см. Windows Collations) все сравнение и сортировка строк выполняются в соответствии с правилами Unicode, даже если на диске представлено CHAR.

Что касается других символов в вашем примере, like VULGAR FRACTION ONE QUARTERи тому подобное они не сравниваются равными ни одному из чисел, но, как уже показал Марк, они сортируют должным образом между 0 и 9.

И, конечно, если вы измените кодовую страницу, вы получите другие результаты. Например. с помощью Greek_CS_AS( кодовая страница 1253 ) вы получите символы с кодами 178, 179 и 189.

Ремус Русану
источник