Warum sind Nicht-Ziffern LIKE [0-9]?

13

Die Standardkollatierung meines Servers ist Latin1_General_CI_AS, wie durch diese Abfrage bestimmt:

SELECT SERVERPROPERTY('Collation') AS Collation;

Es hat mich überrascht, dass ich mit dieser Kollatierung Zeichen in Zeichenfolgen, die keine Ziffern sind, anhand des Prädikats abgleichen kann LIKE '[0-9]'.

Warum geschieht dies in der Standardsortierung? Ich kann mir keinen Fall vorstellen, in dem dies nützlich wäre. Ich weiß, dass ich das Verhalten mit einer binären Kollatierung umgehen kann, aber es scheint eine seltsame Art zu sein, die Standardkollatierung zu implementieren.

Durch das Filtern von Ziffern werden nicht-stellige Zeichen erzeugt

Ich kann das Verhalten veranschaulichen, indem ich eine Spalte erstelle, die alle möglichen Einzelbytezeichenwerte enthält, und die Werte mit dem Prädikat für die Ziffernübereinstimmung filtere.

Die folgende Anweisung erstellt eine temporäre Tabelle mit 256 Zeilen, eine für jeden Codepunkt in der aktuellen Codepage:

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;

Jede Zeile enthält den ganzzahligen Wert des Codepunkts und den Zeichenwert des Codepunkts. Nicht alle Zeichenwerte können angezeigt werden. Einige der Codepunkte sind ausschließlich Kontrollzeichen. Hier ist eine Auswahl der Ausgabe von 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 ÿ

Ich würde erwarten, in der Lage zu sein, in der Symbolspalte nach Ziffern zu filtern, indem ich ein LIKE-Prädikat verwende und den Bereich der Zeichen '0' bis '9' spezifiziere:

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

Es erzeugt eine überraschende Ausgabe:

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 ¾

Die Codepunkte 48 bis 57 sind die, die ich erwarte. Was mich überrascht, ist, dass die Symbole für Hoch- und Nachkommastellen auch in der Ergebnismenge enthalten sind!

Es mag einen mathematischen Grund geben, Exponenten und Brüche als Zahlen zu betrachten, aber es scheint falsch, sie Ziffern zu nennen.

Verwenden der binären Kollatierung als Problemumgehung

Ich verstehe, dass ich die entsprechende binäre Kollatierung Latin1_General_BIN erzwingen kann, um das erwartete Ergebnis zu erhalten:

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

Die Ergebnismenge enthält nur die Codepunkte 48 bis 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
quelle

Antworten:

22

[0-9] Es handelt sich nicht um einen regulären Ausdruck, der so definiert ist, dass er nur mit Ziffern übereinstimmt.

Jeder Bereich in einem LIKEMuster stimmt mit den Zeichen zwischen dem Start- und dem Endzeichen gemäß der Sortierreihenfolge der Sortierung überein.

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 

Kehrt zurück

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

Sie erhalten diese Ergebnisse, weil diese Zeichen in Ihrer Standardkollatierung nach, 0aber vor sortiert sind 9.

Es sieht so aus, als ob die Kollatierung so definiert ist, dass sie tatsächlich in mathematischer Reihenfolge mit den Brüchen in der richtigen Reihenfolge zwischen 0und sortiert wird 1.

Sie können auch einen Satz anstelle eines Bereichs verwenden. Um 2Übereinstimmungen zu vermeiden ², benötigen Sie eine CSKollatierung

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

Latin1 ist Codepage 1252, in der 178 'SUPERSCRIPT TWO' ist . Dies ist ein hochgestellter Unicode- Code : Ist das Zeichen "2" hochgestellt . Gemäß dem technischen Unicode-Standard Nr. 10 sollte er gleich 2 sein, siehe 8.1 Kollatierungsfaltung :

Zuordnungskompatibilitätsäquivalente (tertiäre), wie Zeichen voller Breite und hochgestellte Zeichen , zu repräsentativen Zeichen

Der Fehler wäre, wenn hochgestellte 2 sich von 2 unterscheiden würde! Bevor Sie sagen "Meine Spalte ist jedoch kein Unicode", können Sie sich sicher sein: Gemäß MSDN (siehe Windows-Sortierungen) werden alle Zeichenfolgen nach den Unicode-Regeln verglichen und sortiert, auch wenn die Datenträgerdarstellung CHAR ist.

Die anderen Zeichen in Ihrem Beispiel, like VULGAR FRACTION ONE QUARTERund dergleichen, werden nicht mit einer beliebigen Zahl verglichen, aber, wie Mark bereits gezeigt hat, werden sie ordnungsgemäß zwischen 0 und 9 sortiert.

Und wenn Sie die Codepage ändern, erhalten Sie natürlich andere Ergebnisse. Z.B. mit Greek_CS_AS( Codepage 1253 ) würden Sie die Zeichen mit den Codes 178, 179 und 189 erhalten.

Remus Rusanu
quelle