Perché le non cifre SONO COME [0-9]?


13

Le regole di confronto predefinite del mio server sono Latin1_General_CI_AS, come determinato da questa query:

SELECT SERVERPROPERTY('Collation') AS Collation;

Sono stato sorpreso di scoprire che con questo confronto posso abbinare caratteri non numerici nelle stringhe usando il predicato LIKE '[0-9]'.

Perché nelle regole di confronto predefinite succede? Non riesco a pensare a un caso in cui questo sarebbe utile. So di poter aggirare il comportamento utilizzando una raccolta binaria, ma sembra uno strano modo di implementare la raccolta predefinita.

Il filtraggio delle cifre produce caratteri non numerici

Posso dimostrare il comportamento creando una colonna che contiene tutti i possibili valori dei caratteri a byte singolo e filtrando i valori con il predicato di corrispondenza delle cifre.

La seguente istruzione crea una tabella temporanea con 256 righe, una per ogni punto di codice nella tabella codici corrente:

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;

Ogni riga contiene il valore intero del punto di codice e il valore del carattere del punto di codice. Non tutti i valori dei caratteri sono visualizzabili - alcuni dei punti del codice sono rigorosamente caratteri di controllo. Ecco un esempio selettivo dell'output di 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 ÿ

Mi aspetterei di poter filtrare nella colonna Simbolo per trovare caratteri numerici usando un predicato LIKE e specificando l'intervallo di caratteri da '0' a '9':

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

Produce un risultato sorprendente:

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'insieme di punti di codice da 48 a 57 sono quelli che mi aspetto. Ciò che mi sorprende è che i simboli per apice e frazioni siano inclusi anche nel set di risultati!

Potrebbe esserci una ragione matematica per pensare agli esponenti e alle frazioni come numeri, ma sembra sbagliato chiamarli cifre.

Utilizzo delle regole di confronto binarie come soluzione alternativa

Capisco che per ottenere il risultato che mi aspetto, posso forzare la corrispondenza binaria corrispondente Latin1_General_BIN:

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

Il set di risultati include solo i punti di codice da 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

Risposte:


22

[0-9] non è un tipo di espressione regolare definita per abbinare solo le cifre.

Qualsiasi intervallo in un LIKEmodello corrisponde ai caratteri tra il carattere iniziale e finale in base all'ordinamento delle regole di confronto.

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 

ritorna

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

Quindi ottieni questi risultati perché in base alle regole di confronto predefinite questi personaggi vengono ordinati dopo 0ma prima 9.

Sembra che la collazione sia definita per ordinarli effettivamente in ordine matematico con le frazioni nell'ordine corretto tra 0e 1.

È inoltre possibile utilizzare un set anziché un intervallo. Per evitare l' 2abbinamento ²è necessario un CSconfronto

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

6

Latin1 è la code page 1252, in cui 178 è 'SUPERSCRIPT TWO' . Questo è un apice Unicode : è il carattere "2" come apice . Secondo lo standard tecnico Unicode n. 10 , dovrebbe essere uguale a 2, vedere 8.1 Piegatura delle fascicoli :

Mappare gli equivalenti di compatibilità (terziari), come caratteri a larghezza intera e apice , a caratteri rappresentativi

Il bug sarebbe se l'apice 2 comparasse diverso da 2! Prima di dire "ma la mia colonna non è Unicode", sii certo: secondo MSDN (vedi Raccolte di Windows) tutto il confronto e l'ordinamento delle stringhe vengono eseguiti secondo le regole Unicode, anche quando la rappresentazione su disco è CHAR.

Per quanto riguarda gli altri caratteri nel tuo esempio, like VULGAR FRACTION ONE QUARTERe simili non si equivalgono a nessun numero, ma, come ha già mostrato Mark, si ordinano correttamente tra 0 e 9.

E, naturalmente, se cambiassi la tabella codici otterresti risultati diversi. Per esempio. con Greek_CS_AS( code page 1253 ) otterrai i caratteri con i codici 178, 179 e 189.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.