Чому нецифрові ЛІКУЮТЬ [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), всі порівняння рядків і сортування рядків проводяться згідно з правилами Unicode, навіть коли представлення на диску є CHAR.

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

І, звичайно, якщо ви змінили сторінку коду, ви отримаєте різні результати. Напр. з Greek_CS_AS( кодова сторінка 1253 ), ви отримаєте символи з кодами 178, 179 та 189.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.