O agrupamento padrão do meu servidor é Latin1_General_CI_AS, conforme determinado por esta consulta:
SELECT SERVERPROPERTY('Collation') AS Collation;
Fiquei surpreso ao descobrir que, com esse agrupamento, posso corresponder a caracteres não numéricos em strings usando o predicado LIKE '[0-9]'
.
Por que no agrupamento padrão isso acontece? Não consigo pensar em um caso em que isso seria útil. Sei que posso contornar o comportamento usando um agrupamento binário, mas parece uma maneira estranha de implementar o agrupamento padrão.
A filtragem de dígitos produz caracteres não dígitos
Posso demonstrar o comportamento criando uma coluna que contém todos os valores possíveis de caractere de byte único e filtrando os valores com o predicado de correspondência de dígitos.
A instrução a seguir cria uma tabela temporária com 256 linhas, uma para cada ponto de código na página de código atual:
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;
Cada linha contém o valor inteiro do ponto de código e o valor do caractere do ponto de código. Nem todos os valores de caracteres podem ser exibidos - alguns dos pontos de código são estritamente caracteres de controle. Aqui está uma amostra seletiva da saída 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 ÿ
Eu esperaria ser capaz de filtrar a coluna Symbol para encontrar caracteres de dígitos usando um predicado LIKE e especificando o intervalo de caracteres '0' a '9':
SELECT CodePoint, Symbol
FROM #CodePage
WHERE Symbol LIKE '[0-9]';
Produz uma saída surpreendente:
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 ¾
O conjunto de pontos de código 48 a 57 é o que eu espero. O que me surpreende é que os símbolos para sobrescritos e frações também estão incluídos no conjunto de resultados!
Pode haver uma razão matemática para pensar em expoentes e frações como números, mas parece errado chamá-los de dígitos.
Usando o agrupamento binário como uma solução alternativa
Entendo que, para obter o resultado esperado, posso forçar o agrupamento binário correspondente Latin1_General_BIN:
SELECT CodePoint, Symbol
FROM #CodePage
WHERE Symbol LIKE '[0-9]' COLLATE Latin1_General_BIN;
O conjunto de resultados inclui apenas os pontos de código 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]
não é algum tipo de expressão regular definida apenas para corresponder a dígitos.Qualquer intervalo em um
LIKE
padrão corresponde aos caracteres entre o caractere inicial e final de acordo com a ordem de classificação do agrupamento.devoluções
Portanto, você obtém esses resultados porque, em seu agrupamento padrão, esses caracteres são classificados depois,
0
mas antes9
.Parece que o agrupamento é definido para realmente classificá-los em ordem matemática com as frações na ordem correta entre
0
e1
.Você também pode usar um conjunto em vez de um intervalo. Para evitar a
2
correspondência²
, você precisaria de umCS
agrupamentoLatin1 é a página de código 1252, na qual 178 é 'SUPERSCRIPT TWO' . Este é um sobrescrito Unicode : é o caractere "2" como sobrescrito . De acordo com o Padrão Técnico Unicode nº 10 , deve ser comparado igual a 2, consulte 8.1 Collation Folding :
O bug seria se o sobrescrito 2 fosse comparado diferente de 2! Antes de dizer 'mas minha coluna não é Unicode', fique tranquilo: de acordo com o MSDN (consulte Windows Collations), todas as comparações e classificações de strings são feitas de acordo com as regras do Unicode, mesmo quando a representação no disco é CHAR.
Quanto aos outros caracteres do seu exemplo, like
VULGAR FRACTION ONE QUARTER
e like eles não comparam igual a nenhum número, mas, como Mark já mostrou, eles classificam corretamente entre 0 e 9.E, é claro, se você alterasse a página de código, obteria resultados diferentes. Por exemplo. com
Greek_CS_AS
( página de código 1253 ) você obteria os caracteres com o código 178, 179 e 189.