Quelle est la logique derrière ISNUMERIC pour certains caractères spéciaux?

14

La ISNUMERICfonction a un comportement inattendu. La documentation MSDN indique:

ISNUMERICrenvoie 1 lorsque l'expression d'entrée est évaluée en un type de données numérique valide; sinon, il renvoie 0. Les types de données numériques valides sont les suivants: int, bigint, smallint, tinyint, decimal, numeric, money, smallmoney, float, real .

Et il a également une note de bas de page:

ISNUMERICrenvoie 1 pour certains caractères qui ne sont pas des nombres, tels que plus (+), moins (-) et des symboles monétaires valides tels que le signe dollar ($). Pour une liste complète des symboles monétaires, consultez money and smallmoney (Transact-SQL) .

D'accord, donc +, -et les symboles monétaires répertoriés devraient être considérés comme numériques. Jusqu'ici tout va bien.

Maintenant pour la partie étrange. Tout d'abord, certains des symboles monétaires de l'article lié ne sont pas numériques, notamment:

  • Signe euro-monnaie, hex 20A0:
  • Signe Naira, hex 20A6:
  • Signe Rial, hex FDFC:

C'est bizarre, et je n'arrive pas à découvrir pourquoi? Cette version ou l'environnement dépend-il?

Cependant, les choses deviennent plus étranges. Voici quelques autres que je ne peux pas expliquer:

  • /n'est pas numérique, mais l' \est ( Huh?! )
  • REPLICATE(N'9', 308)est numérique, mais REPLICATE(N'9', 309)n'est pas

La première question, la plus fondamentale, est: qu'est-ce qui explique les cas ci-dessus? Mais plus important encore: quelle est la logique derrièreISNUMERIC , afin que je puisse expliquer / prédire tous les cas moi-même?

Voici un bon moyen de reproduire des choses:

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;

Lorsque j'exécute cela sur ma boîte Sql Server 2012 locale, j'obtiens les résultats suivants:

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
Jeroen
la source
Les seuls qui me semblent incorrects, c'est qu'ils rapportent faussement 0cinq des valeurs qui conviennent réellement money. Les autres semblent exacts. SQL FIDDLE
Martin Smith
Bien que NCHAR(0) - NCHAR(65535)je vois 112 écarts. Y compris des caractères tels que ceux ₁,₂,₃,4,5,6,7,8,9qui semblent numériques mais qui ne réussissent pas à quelque chose pour moi. Violon
Martin Smith

Réponses:

13

Les comportements détaillés de ISNUMERIC ne sont pas documentés et ne sont probablement pas entièrement connus de quiconque sans accès au code source. Cela dit, il se peut que l'interprétation dépende de la catégorisation Unicode (numérique ou non). De même, les cas étranges que vous mentionnez peuvent être des bogues qui sont conservés pour une compatibilité descendante. Oui, je sais que cela semble fou, mais cela arrive.

Comme vous utilisez SQL Server 2012, il n'est pas nécessaire de l'utiliser ISNUMERIC. Utilisez TRY_CONVERTou également à la TRY_CASTplace pour vérifier si une chaîne est convertible en un type donné. Lorsqu'elles fournissent des fonctionnalités adéquates, elles sont préférables à TRY_PARSE, car ces dernières impliquent un traitement plus coûteux via l'intégration CLR.

Paul White 9
la source
2
Et probablement pas entièrement connu de beaucoup de gens avec un accès au code source. :-) J'aimerais pouvoir +1 à nouveau pour le deuxième paragraphe. ISNUMERIC () est largement inutile car son intention est de déterminer si quelque chose peut être converti en au moins un type numérique; il est évidemment beaucoup plus important de savoir que vous pouvez convertir en un seul type numérique spécifique.
Aaron Bertrand
1
@AaronBertrand Il semble y avoir un nombre raisonnablement important de cas où il ne répond même pas à cette intention.
Martin Smith
9

La barre oblique inversée ASCII (point de code 5C) partage le même point de code que le signe yen (¥) dans l'encodage Shift-JIS utilisé par la version japonaise de Windows, et le signe won (₩) en coréen EUC-KR. Par conséquent, il s'agit très probablement d'une continuation du thème du signe monétaire.

user47620
la source
Ah c'est une théorie intéressante. C'est moneyqu'il s'applique également.
Martin Smith
@Jeroen - C'est sur Wikipedia FWIW
Martin Smith
3
@Jeroen J'ai bien peur que non. Basculez la page de codes héritée de votre installation Windows vers le japonais et vous obtenez des chemins tels que C:¥Program Files¥dans explorer.exe
user47620