对于某些特殊字符,ISNUMERIC背后的逻辑是什么?


14

ISNUMERIC函数具有某些意外行为。MSDN文档说:

ISNUMERIC当输入表达式的计算结果为有效的数值数据类型时,返回1;否则返回0。有效的数字数据类型包括以下类型:int,bigint,smallint,tinyint,十进制,数字,money,smallmoney,float,real

它也有一个脚注:

ISNUMERIC对于非数字字符(例如加号(+),减号(-))和有效的货币符号(例如美元符号($)),返回1。有关货币符号的完整列表,请参见money和s​​mallmoney(Transact-SQL)

好的,因此+-和列出的货币符号应被视为数字。到目前为止,一切都很好。

现在是奇怪的部分。首先,链接文章中的某些货币符号不是数字,包括:

  • 欧元货币符号,十六进制20A0:
  • 奈拉符号,十六进制20A6:
  • 里亚尔符号,十六进制FDFC:

这很奇怪,我似乎找不到原因?此版本或环境相关吗?

但是,事情变得奇怪了。这里有一些我无法解释的内容:

  • /不是数字,而是\呵呵!
  • REPLICATE(N'9', 308)是数字,但REPLICATE(N'9', 309)不是

第一个也是最基本的问题是:什么能解释上述情况?但更重要的是:背后的逻辑是什么ISNUMERIC,以便我自己解释/预测所有情况?

这是重现事物的好方法:

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;

当我在本地Sql Server 2012框上运行此命令时,得到以下结果:

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

在我看来唯一不正确的是,它错误地报告0了实际上精确到的五个值money。其他似乎准确。SQL FIDDLE
Martin Smith

虽然从NCHAR(0) - NCHAR(65535)我看到112个差异。包括₁,₂,₃,4,5,6,7,8,9看起来像数字但不能成功转换为任何字符的字符。小提琴
马丁·史密斯

Answers:


13

ISNUMERIC没有记录详细的行为,没有源代码访问权限的任何人可能都不完全了解。就是说,解释可能取决于Unicode分类(是否为数字)。同样,您提到的怪异案例可能是为向后兼容而保留的错误。是的,我知道这听起来很疯狂,但确实如此。

在使用SQL Server 2012时,无需使用ISNUMERIC。使用TRY_CONVERT或同义词TRY_CAST来检查字符串是否可转换为给定类型。如果它们提供足够的功能,则它们比更为可取TRY_PARSE,因为后者需要通过CLR集成进行更昂贵的处理。


2
也可能不是很多具有源代码访问权限的人所熟知的。:-)希望第二段我可以再次+1。ISNUMERIC()在很大程度上是无用的,因为它的目的是确定某个东西是否可以转换为至少一种数字类型。显然可以知道您可以转换为单个特定的数字类型,这要重要得多。
亚伦·伯特兰

1
@AaronBertrand似乎在相当多的情况下,它甚至也达不到该目的。
马丁·史密斯

9

ASCII反斜杠(代码点5C)恰好与日语版Windows所使用的Shift-JIS编码中的日元符号(¥)和韩语EUC-KR中的韩元符号(₩)共享相同的代码点。因此,很有可能仅仅是货币符号主题的延续。


啊,这是一个有趣的理论。它也是money强制转换的。
Martin Smith

@Jeroen- 在Wikipedia FWIW上
Martin Smith

3
@Jeroen恐怕不是。将Windows安装的旧版代码页切换为日语,您会获得诸如C:¥Program Files¥explorer.exe之类的路径
user47620 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.