Welche Logik steckt hinter ISNUMERIC für bestimmte Sonderzeichen?

14

Die ISNUMERICFunktion hat ein unerwartetes Verhalten. In der MSDN-Dokumentation heißt es:

ISNUMERICGibt 1 zurück, wenn der Eingabeausdruck einen gültigen numerischen Datentyp ergibt. Andernfalls wird 0 zurückgegeben. Zu den gültigen numerischen Datentypen gehören: int, bigint, smallint, tinyint, decimal, numerisch, money, smallmoney, float, real .

Und es hat auch eine Fußnote:

ISNUMERICGibt 1 für einige Zeichen zurück, die keine Zahlen sind, z. B. Plus (+), Minus (-) und gültige Währungssymbole wie das Dollarzeichen ($). Eine vollständige Liste der Währungssymbole finden Sie unter Geld und Kleingeld (Transact-SQL) .

Okay, also +, -und aufgelistete Währungssymbole werden voraussichtlich als numerisch betrachtet. So weit, ist es gut.

Nun zum merkwürdigen Teil. Erstens sind einige der Währungssymbole aus verknüpften Artikeln nicht numerisch, einschließlich:

  • Euro-Währungszeichen, hex 20A0:
  • Naira-Zeichen, Hex 20A6:
  • Rial Zeichen hex FDFC:

Das ist komisch, und ich kann nicht herausfinden, warum? Ist diese Version oder Umgebung abhängig?

Es wird jedoch seltsamer. Hier sind ein paar andere, die ich nicht erklären kann:

  • /ist nicht numerisch, ist aber \( Huh ?! )
  • REPLICATE(N'9', 308)ist numerisch, ist es aber REPLICATE(N'9', 309)nicht

Die erste und grundlegendste Frage lautet: Was erklärt die obigen Fälle? Wichtiger jedoch: Welche Logik steckt dahinterISNUMERIC , sodass ich alle Fälle selbst erklären / vorhersagen kann?

Hier ist ein guter Weg, um Dinge zu reproduzieren:

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;

Wenn ich dies auf meiner lokalen SQL Server 2012-Box ausführe, erhalte ich die folgenden Ergebnisse:

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
quelle
Die einzigen, die mir unkorrekt erscheinen, sind die, dass fälschlicherweise 0fünf der Werte angegeben werden, für die tatsächlich ein Fehler vorliegt money. Die anderen scheinen genau zu sein. SQL FIDDLE
Martin Smith
Allerdings NCHAR(0) - NCHAR(65535)sehe ich 112 Unstimmigkeiten. Einschließlich Zeichen, ₁,₂,₃,4,5,6,7,8,9die numerisch aussehen, aber für mich nicht erfolgreich umgewandelt werden. Fiddle
Martin Smith

Antworten:

13

Die detaillierten Verhaltensweisen von ISNUMERICsind nicht dokumentiert und wahrscheinlich niemandem ohne Quellcode-Zugriff vollständig bekannt. Das heißt, es kann sein, dass die Interpretation von der Unicode-Kategorisierung abhängt (numerisch oder nicht). Ebenso können die seltsamen Fälle, die Sie erwähnen, Fehler sein, die aus Gründen der Abwärtskompatibilität beibehalten werden. Ja, ich weiß, das klingt verrückt, aber es passiert.

Da Sie SQL Server 2012 verwenden, ist dies nicht erforderlich ISNUMERIC. Verwenden Sie TRY_CONVERToder auch TRY_CAST, um zu überprüfen, ob eine Zeichenfolge in einen bestimmten Typ konvertierbar ist. Sofern sie eine ausreichende Funktionalität bieten, sind diese vorzuziehen TRY_PARSE, da letztere eine teurere Verarbeitung über die CLR-Integration beinhaltet.

Paul White Monica wieder einsetzen
quelle
2
Und wahrscheinlich auch vielen Leuten mit Quellcode-Zugang nicht vollständig bekannt. :-) Wünschte, ich könnte nochmal +1 für den zweiten Absatz geben. ISNUMERIC () ist weitgehend unbrauchbar, da festgestellt werden soll, ob etwas in mindestens einen numerischen Typ konvertiert werden kann. Es ist natürlich viel wichtiger zu wissen, dass Sie in einen einzelnen, spezifischen numerischen Typ konvertieren können.
Aaron Bertrand
1
@AaronBertrand Es scheint eine einigermaßen signifikante Anzahl von Fällen zu geben, in denen diese Absicht auch nicht erfüllt wird.
Martin Smith
9

Der ASCII-umgekehrte Schrägstrich (Codepunkt 5C) hat denselben Codepunkt wie das Yen-Zeichen (¥) in der von der japanischen Windows-Version verwendeten Shift-JIS-Codierung und das Won-Zeichen (₩) in der koreanischen EUC-KR. Daher ist es sehr wahrscheinlich nur eine Fortsetzung des Währungszeichenthemas.

user47620
quelle
Ah, das ist eine interessante Theorie. Es ist, moneydass es auch wirkt.
Martin Smith
@ Jeroen - Es ist auf Wikipedia FWIW
Martin Smith
3
@Jeroen Ich fürchte nicht. C:¥Program Files¥
Wechseln