Qual è la logica dietro ISNUMERIC per alcuni personaggi speciali?


14

La ISNUMERICfunzione ha alcuni comportamenti imprevisti. La documentazione MSDN dice:

ISNUMERICrestituisce 1 quando l'espressione di input restituisce un tipo di dati numerico valido; in caso contrario restituisce 0. I tipi di dati numerici validi includono quanto segue: int, bigint, smallint, tinyint, decimal, numeric, money, smallmoney, float, real .

E ha anche una nota a piè di pagina:

ISNUMERICrestituisce 1 per alcuni caratteri che non sono numeri, come più (+), meno (-) e simboli di valuta validi come il simbolo del dollaro ($). Per un elenco completo dei simboli di valuta, vedere denaro e smallmoney (Transact-SQL) .

Ok, quindi +, -e i simboli di valuta elencati dovrebbero essere considerati numerici. Fin qui tutto bene.

Ora per la parte dispari. Innanzitutto, alcuni dei simboli di valuta dell'articolo collegato non sono numerici, tra cui:

  • Segno di valuta Euro, esadecimale 20A0:
  • Segno di Naira, esagono 20A6:
  • Segno rial, esadecimale FDFC:

È strano e non riesco a capire perché? Questa versione o l'ambiente dipende?

Tuttavia, le cose diventano più strane. Eccone alcuni che non posso spiegare:

  • /non è numerico, ma \è ( eh ?! )
  • REPLICATE(N'9', 308)è numerico, ma REPLICATE(N'9', 309)non lo è

La prima e più fondamentale domanda è: che cosa spiega i casi di cui sopra? Ancora più importante: qual è la logica alla baseISNUMERIC , in modo che io possa spiegare / prevedere tutti i casi da solo?

Ecco un buon modo per riprodurre le cose:

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 eseguo questo sulla mia casella locale SQL Server 2012 ottengo i seguenti risultati:

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

Le uniche che mi sembrano errate sono che riporta erroneamente 0cinque dei valori che effettivamente vanno bene money. Gli altri sembrano precisi. SQL FIDDLE
Martin Smith,

Anche se NCHAR(0) - NCHAR(65535)vedo 112 discrepanze. Compresi personaggi come quelli ₁,₂,₃,4,5,6,7,8,9che sembrano numerici ma che non trasmettono con successo a nulla per me. Fiddle
Martin Smith,

Risposte:


13

I comportamenti dettagliati di ISNUMERICnon sono documentati e probabilmente non sono completamente noti a nessuno senza accesso al codice sorgente. Detto questo, può darsi che l'interpretazione dipenda dalla categorizzazione Unicode (numerica o meno). Allo stesso modo, i casi strani che menzioni potrebbero essere bug che vengono conservati per compatibilità con le versioni precedenti. Sì, lo so che sembra folle, ma succede.

Poiché si utilizza SQL Server 2012, non è necessario utilizzarlo ISNUMERIC. Utilizzare TRY_CONVERTo anche TRY_CASTinvece per verificare se una stringa è convertibile in un determinato tipo. Laddove forniscono una funzionalità adeguata, è preferibile farlo TRY_PARSE, poiché quest'ultimo comporta un'elaborazione più costosa tramite l'integrazione CLR.


2
E probabilmente non è completamente noto a molte persone con accesso al codice sorgente. :-) Vorrei poter fare nuovamente +1 per il secondo paragrafo. ISNUMERIC () è in gran parte inutile perché la sua intenzione è determinare se qualcosa può essere convertito in almeno un tipo numerico; è ovviamente molto più importante sapere che è possibile convertire in un singolo tipo numerico specifico.
Aaron Bertrand

1
@AaronBertrand Sembra esserci un numero ragionevolmente significativo di casi in cui non soddisfa nemmeno questa intenzione.
Martin Smith,

9

La barra rovesciata ASCII (punto codice 5C) sembra condividere lo stesso punto codice del segno yen (¥) nella codifica Shift-JIS utilizzata dalla versione giapponese di Windows e il segno vinto (₩) nella coreana EUC-KR. Quindi, è molto probabilmente solo una continuazione del tema del segno di valuta.


Ah, questa è una teoria interessante. È moneyche lancia anche.
Martin Smith,

@Jeroen - È su Wikipedia FWIW
Martin Smith,

3
@Jeroen, temo di no. Cambia la code page legacy dell'installazione di Windows in giapponese e ottieni percorsi come C:¥Program Files¥in explorer.exe
user47620
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.