A ISNUMERIC
função tem algum comportamento inesperado. A documentação do MSDN diz:
ISNUMERIC
retorna 1 quando a expressão de entrada é avaliada como um tipo de dados numérico válido; caso contrário, retorna 0. Os tipos de dados numéricos válidos incluem os seguintes: int, bigint, smallint, tinyint, decimal, numeric, money, smallmoney, float, real .
E também tem uma nota de rodapé:
ISNUMERIC
retorna 1 para alguns caracteres que não são números, como mais (+), menos (-) e símbolos de moeda válidos, como o cifrão ($). Para obter uma lista completa de símbolos de moeda, consulte money e smallmoney (Transact-SQL) .
Ok, então espera-se que +
, -
e os símbolos de moeda listados sejam considerados numéricos. Até agora tudo bem.
Agora para a parte estranha. Em primeiro lugar, alguns dos símbolos de moeda do artigo vinculado não são numéricos, incluindo:
- Sinal de euro-moeda, hex 20A0:
₠
- Sinal de Naira, hex 20A6:
₦
- Sinal Rial, hexadecimal FDFC:
﷼
Isso é estranho, e eu não consigo descobrir por quê? Esta versão ou ambiente depende?
No entanto, as coisas ficam mais estranhas. Aqui estão alguns outros que não consigo explicar:
/
não é numérico, mas\
é ( né?! )REPLICATE(N'9', 308)
é numérico, masREPLICATE(N'9', 309)
não é
A primeira e mais básica pergunta é: o que explica os casos acima? Mais importante, porém: qual é a lógica por trás doISNUMERIC
, para que eu possa explicar / prever todos os casos sozinho?
Aqui está uma boa maneira de reproduzir as coisas:
DECLARE @tbl TABLE(txt NVARCHAR(1000));
INSERT INTO @tbl (txt)
VALUES (N''), (N' '), (N'€'), (N'$'), (N'$$'),
(NCHAR(8356)), (NCHAR(8352)), (NCHAR(8358)), (NCHAR(65020)),
(N'+'), (N'-'), (N'/'), (N'\'), (N'_'), (N'e'), (N'1e'), (N'e1'), (N'1e1'),
(N'1'), (N'-1'), (N'+1'), (N'1+1'), (N'⒈'), (N'?'), (N'¹'), (N'①'), (N'½'),
(N'?'), (REPLICATE(N'9', 307)), (REPLICATE(N'9', 308)), (REPLICATE(N'9', 309)),
(REPLICATE(N'9', 310));
SELECT UNICODE(LEFT(txt, 1)) AS FirstCharAsInt,
LEN(txt) AS TxtLength,
txt AS Txt,
ISNUMERIC(txt) AS [ISNUMERIC]
FROM @tbl;
Quando executo isso na minha caixa local do SQL Server 2012, obtenho os seguintes resultados:
FirstCharAsInt TxtLength Txt ISNUMERIC
--------------- ---------- --------- ----------
NULL 0 0
32 0 0
8364 1 € 1
36 1 $ 1
36 2 $$ 0
8356 1 ₤ 1
8352 1 ₠ 0 --??
8358 1 ₦ 0 --??
65020 1 ﷼ 0 --??
43 1 + 1
45 1 - 1
47 1 / 0
92 1 \ 1 --??
95 1 _ 0
101 1 e 0
49 2 1e 0
101 2 e1 0
49 3 1e1 1
49 1 1 1
45 2 -1 1
43 2 +1 1
49 3 1+1 0
9352 1 ⒈ 0
55356 2 ? 0
185 1 ¹ 0
9312 1 ① 0
189 1 ½ 0
55356 2 ? 0
57 307 /*...*/ 1
57 308 /*...*/ 1 --??
57 309 /*...*/ 0 --??
57 310 /*...*/ 0
Os comportamentos detalhados de
ISNUMERIC
não estão documentados e provavelmente não são totalmente conhecidos por ninguém sem acesso ao código-fonte. Dito isso, pode ser que a interpretação dependa da categorização Unicode (numérica ou não). Da mesma forma, os casos estranhos que você mencionou podem ser bugs preservados para compatibilidade com versões anteriores. Sim, eu sei que parece loucura, mas acontece.Como você está usando o SQL Server 2012, não há necessidade de usar
ISNUMERIC
. Em vez disso, useTRY_CONVERT
ou o sinônimoTRY_CAST
para verificar se uma string pode ser convertida em um determinado tipo. Onde eles fornecem funcionalidade adequada, eles são preferíveis aTRY_PARSE
, porque o último envolve processamento mais caro via integração CLR.A barra invertida ASCII (ponto de código 5C) compartilha o mesmo ponto de código que o sinal de iene (¥) na codificação Shift-JIS usada pela versão japonesa do Windows e o sinal won (₩) em coreano EUC-KR. Portanto, é muito provável que seja apenas uma continuação do tema do símbolo da moeda.