ISNUMERIC对'$123'、'1e4'、' '等返回1,因其设计目标是判断字符串能否隐式转换为任意数字类型(如MONEY、FLOAT),而非是否为纯数字字符串;它不保证CAST/CONVERT成功,故推荐SQL Server 2012+使用TRY_CAST或TRY_CONVERT进行安全转换。

ISNUMERIC 为什么对 '$123'、'1e4'、' ' 都返回 1
因为 ISNUMERIC 的设计目标根本不是“判断是否为纯数字字符串”,而是“能否隐式转换成任意一种数字类型(INT、FLOAT、MONEY、REAL 等)”。它不关心业务语义,只看 SQL Server 内部类型系统是否接受该字符串作为某类数字的输入。
典型误判包括:
-
ISNUMERIC('$123')→ 1(可转为MONEY,但不能转INT) -
ISNUMERIC('1e4')→ 1(可转为FLOAT,但CAST('1e4' AS INT)直接报错) -
ISNUMERIC(' ')→ 1(空格在某些排序规则下被当作可转FLOAT的合法前导/尾随空白) -
ISNUMERIC(',')→ 1(千分位逗号在MONEY上下文中合法) -
ISNUMERIC('-')→ 1(单独负号也能被解析为FLOAT的起始符号)
WHERE 中用 ISNUMERIC = 1 + CAST 组合会崩溃
写 WHERE ISNUMERIC(col) = 1 AND CAST(col AS INT) > 100 看似合理,但 SQL Server 查询优化器不保证执行顺序。它可能先尝试对 '1e4' 执行 CAST,再检查 ISNUMERIC,结果直接报错:“从数据类型 varchar 转换为 int 时出错”。
这不是竞态,是语义不可靠:函数之间无逻辑依赖保障。
正确做法是把转换和判断合并为原子操作:
- SQL Server 2012+:
WHERE TRY_CAST(col AS INT) > 100 - 旧版本无法避免两步,必须用
CASE WHEN ISNUMERIC(...) = 1 THEN CAST(...) ELSE NULL END包裹,再在外层比较
PATINDEX 做纯数字校验要注意边界情况
想严格匹配“非负整数”,常写 PATINDEX('%[^0-9]%', col) = 0,但它漏掉几个关键点:
- 空字符串
''和全空白(如' ')都会让PATINDEX返回 0,需额外加LEN(LTRIM(RTRIM(col))) > 0 - 全角数字(如‘123’)会被当成非数字字符,因 Unicode 编码不在
[0-9]范围内 - 支持小数点或负号时,
PATINDEX('%[^0-9.-]%', col)仍会放过'123.'、'-.5'、'1..2'这类非法格式 -
PATINDEX不校验数值范围(如超长整数溢出),仅做字符层面过滤
真正难的不是写判断,而是定义清楚“什么是数字”
你到底要什么?
- 能安全参与
INT运算?→ 用TRY_CAST(col AS INT) IS NOT NULL - 允许小数但禁止科学计数法?→ 先
PATINDEX排除e/E,再TRY_CAST(col AS DECIMAL(18,6)) - 必须是带正负号的整数?→
col LIKE '[+-][0-9]%' AND PATINDEX('%[^0-9]%', SUBSTRING(col, 2, LEN(col))) = 0,再加TRY_CAST验证
别让 ISNUMERIC 模糊掉这些需求差异——它返回 1 只说明“SQL Server 认为它有点像数字”,不代表你的业务能用。

















