Pourquoi les non-chiffres sont-ils COMME [0-9]?

13

Le classement par défaut de mon serveur est Latin1_General_CI_AS, comme déterminé par cette requête:

SELECT SERVERPROPERTY('Collation') AS Collation;

J'ai été surpris de découvrir qu'avec ce classement, je peux faire correspondre des caractères non numériques dans des chaînes en utilisant le prédicat LIKE '[0-9]'.

Pourquoi dans le classement par défaut cela se produit-il? Je ne peux pas penser à un cas où cela serait utile. Je sais que je peux contourner le problème en utilisant un classement binaire, mais cela semble être une façon étrange d'implémenter le classement par défaut.

Le filtrage des chiffres produit des caractères non numériques

Je peux démontrer le comportement en créant une colonne qui contient toutes les valeurs de caractères possibles sur un octet et en filtrant les valeurs avec le prédicat de correspondance de chiffres.

L'instruction suivante crée une table temporaire avec 256 lignes, une pour chaque point de code dans la page de codes actuelle:

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;

Chaque ligne contient la valeur entière du point de code et la valeur de caractère du point de code. Toutes les valeurs de caractères ne peuvent pas être affichées - certains des points de code sont strictement des caractères de contrôle. Voici un échantillon sélectif de la sortie de 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 ÿ

Je m'attendrais à pouvoir filtrer sur la colonne Symbole pour trouver des caractères numériques en utilisant un prédicat LIKE et en spécifiant la plage de caractères de '0' à '9':

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

Il produit une sortie surprenante:

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 ¾

L'ensemble des points de code 48 à 57 sont ceux que j'attends. Ce qui me surprend, c'est que les symboles pour les exposants et les fractions sont également inclus dans le jeu de résultats!

Il peut y avoir une raison mathématique de considérer les exposants et les fractions comme des nombres, mais il semble incorrect de les appeler des chiffres.

Utilisation du classement binaire comme solution de contournement

Je comprends que pour obtenir le résultat attendu, je peux forcer le classement binaire correspondant Latin1_General_BIN:

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

L'ensemble de résultats comprend uniquement les points de code 48 à 57:

CodePoint   Symbol
48  0
49  1
50  2
51  3
52  4
53  5
54  6
55  7
56  8
57  9
Iain Samuel McLean Elder
la source

Réponses:

22

[0-9] n'est pas un type d'expression régulière défini pour correspondre uniquement aux chiffres.

N'importe quelle plage dans un LIKEmodèle correspond aux caractères entre le caractère de début et de fin selon l'ordre de tri de classement.

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 

Retour

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

Vous obtenez donc ces résultats car sous votre classement par défaut, ces caractères sont triés après 0mais avant 9.

Il semble que le classement soit défini pour les trier réellement dans l'ordre mathématique avec les fractions dans l'ordre correct entre 0et 1.

Vous pouvez également utiliser un ensemble plutôt qu'une plage. Pour éviter la 2correspondance, ²vous auriez besoin d'un CSclassement

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

Latin1 est la page de codes 1252, dans laquelle 178 est «SUPERSCRIPT TWO» . Il s'agit d'un exposant Unicode : c'est le caractère "2" en exposant . Selon la norme technique Unicode n ° 10, il doit être égal à 2, voir 8.1 Pliage du classement :

Mappez les équivalents (tertiaires) de compatibilité, tels que les caractères pleine largeur et en exposant , aux caractères représentatifs

Le bogue serait que l'exposant 2 soit différent de 2! Avant de dire `` mais ma colonne n'est pas Unicode '', soyez assuré: selon MSDN (voir Collations Windows), toutes les comparaisons de chaînes et le tri sont effectués conformément aux règles Unicode, même lorsque la représentation sur disque est CHAR.

Quant aux autres personnages de votre exemple, similaires VULGAR FRACTION ONE QUARTERet similaires, ils ne se comparent pas à n'importe quel nombre, mais, comme Mark l'a déjà montré, ils trient correctement entre 0 et 9.

Et, bien sûr, si vous changez la page de codes, vous obtiendrez des résultats différents. Par exemple. avec Greek_CS_AS( page de codes 1253 ) vous obtiendrez les caractères avec les codes 178, 179 et 189.

Remus Rusanu
la source