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

Jer*_*oen 14 sql-server sql-server-2012

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

ISNUMERIC当输入表达式计算为有效的数字数据类型时返回 1;否则返回 0。有效的数字数据类型包括:int、bigint、smallint、tinyint、decimal、numeric、money、smallmoney、float、real

它还有一个脚注:

ISNUMERIC对于某些非数字字符返回 1,例如加号 (+)、减号 (-) 和有效的货币符号,例如美元符号 ($)。有关货币符号的完整列表,请参阅money 和 smallmoney (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;
Run Code Online (Sandbox Code Playgroud)

当我在本地 Sql Server 2012 机器上运行它时,我得到以下结果:

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;
Run Code Online (Sandbox Code Playgroud)

Pau*_*ite 13

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

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

  • 很多拥有源代码访问权限的人可能也不完全了解。:-) 希望我能为第二段再次+1。ISNUMERIC() 在很大程度上是无用的,因为它的目的是确定是否可以将某些内容转换为至少一种数字类型;知道您可以转换为单个特定的数字类型显然要重要得多。 (2认同)

use*_*620 9

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

  • @Jeroen 恐怕不行。将 Windows 安装的旧代码页切换为日语,您将在 explorer.exe 中获得诸如“C:¥Program Files¥”之类的路径 (3认同)