it-swarm-eu.dev

Proč nejsou číslice LIKE [0-9]?

Výchozí řazení mého serveru je Latin1_General_CI_AS, jak je určeno tímto dotazem:

SELECT SERVERPROPERTY('Collation') AS Collation;

Překvapilo mě, když jsem zjistil, že s touto kombinací dokážu porovnávat neciferné znaky v řetězcích pomocí predikátu LIKE '[0-9]'.

Proč se to stane ve výchozím řazení? Nedokážu vymyslet případ, kdy by to bylo užitečné. Vím, že dokážu obejít chování pomocí binárního řazení, ale vypadá to jako zvláštní způsob implementace výchozího řazení.

Filtrování číslic vytváří neciferné znaky

Chci demonstrovat chování vytvořením sloupce, který obsahuje všechny možné hodnoty jednobajtových znaků a filtrováním hodnot pomocí predikátu přiřazování číslic.

Následující příkaz vytvoří dočasnou tabulku s 256 řádky, jednu pro každý kódový bod na aktuální kódové stránce:

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;

Každý řádek obsahuje celočíselnou hodnotu kódového bodu a znakovou hodnotu kódového bodu. Ne všechny hodnoty znaků jsou zobrazitelné - některé z kódových bodů jsou přísně kontrolní znaky. Zde je výběrový vzorek výstupu 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 ÿ

Očekával bych, že budu schopen filtrovat ve sloupci Symbol, aby našel číselné znaky pomocí predikátu LIKE a určil rozsah znaků '0' až '9':

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

Vytváří překvapivý výstup:

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 ¾

Sada kódových bodů 48 až 57 jsou ty, které očekávám. Co mě překvapuje, je, že ve výsledkové sadě jsou také zahrnuty symboly pro horní a dolní index!

Může existovat matematický důvod myslet na exponenty a zlomky jako na čísla, ale zdá se špatné říkat jim číslice.

Použití binárního řazení jako řešení

Chápu, že k dosažení očekávaného výsledku mohu vynutit odpovídající binární řazení Latin1_General_BIN:

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

Sada výsledků obsahuje pouze body kódu 48 až 57:

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

[0-9] není nějaký typ regulárního výrazu definovaný tak, aby odpovídal pouze číslům.

Jakýkoli rozsah ve vzoru LIKE odpovídá znakům mezi počátečním a koncovým znakem podle pořadí řazení.

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 

Vrací se

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

Tyto výsledky získáte, protože podle výchozího řazení se tyto znaky třídí po 0 ale předtím 9.

Vypadá to, že řazení je definováno tak, že je je skutečně třídí v matematickém pořadí se zlomky ve správném pořadí mezi 0 a 1.

Dalo by se také použít spíše sadu než rozsah. Vyhnout se 2 odpovídající ² budete potřebovat CS řazení

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

Latin1 je kódová stránka 1252, ve které 178 je 'SUPERSCRIPT TWO' . Toto je Unicode horní index : je znak "2" jako horní index . Podle nicode Technical Standard # 1 by se měl porovnat roven 2, viz 8.1 Collation Folding :

Ekvivalenty mapy kompatibilní (terciární), , jako jsou znaky s plnou šířkou a horní index , s reprezentativními znaky

Chyba by byla, pokud by horní index 2 byl jiný než 2! Než řeknete „ale můj sloupec není Unicode“, buďte ujištěni: podle MSDN (viz Windows Collations) jsou všechny porovnávání řetězců a třídění prováděny podle Pravidla Unicode, i když je reprezentace na disku CHAR.

Pokud jde o další znaky v příkladu, například VULGAR FRACTION ONE QUARTER a podobně se nesrovnávají s žádným číslem, ale jak již Mark ukázal, pořádně seřadí mezi 0 a 9.

A samozřejmě, pokud změníte kódovou stránku, získáte jiné výsledky. Např. s Greek_CS_AS ( kódová stránka 125 ) dostanete znaky s kódem 178, 179 a 189.

6
Remus Rusanu