应使用 try_cast 或 try_convert 替代 isnumeric,因其能准确判断字符串是否可安全转为指定数字类型;isnumeric 仅判断能否隐式转为任意数字类型,对 '$123'、'1e4' 等返回 1 却导致 cast 报错,且不处理空格、空字符串等常见输入。

直接用 TRY_CAST 或 TRY_CONVERT 判断,别碰 ISNUMERIC —— 它返回 1 的时候,CAST 很可能立刻报错。
为什么 ISNUMERIC 在存储过程中不能信
ISNUMERIC 的设计目标是“能否被隐式转成任意数字类型”,不是“这个字符串能不能安全参与计算”。在存储过程里一旦依赖它做分支逻辑,很容易走到 CAST(@val AS INT) 报错那一步。
常见陷阱:
-
ISNUMERIC('$123')返回 1,但CAST('$123' AS INT)直接抛异常 -
ISNUMERIC('1e4')返回 1,CAST('1e4' AS INT)失败(得用FLOAT) -
ISNUMERIC(' ')或ISNUMERIC(',')在某些排序规则下也返回 1,但根本不是有效输入 - 它不处理空字符串
''和纯空白,而这些在参数传入时极常见
TRY_CAST 是最干净的判断方式(SQL Server 2012+)
它不抛错,失败就返回 NULL,语义清晰,和存储过程里的 IF 判断天然契合。
实操建议:
- 判断是否可转为整数:
TRY_CAST(@input AS INT) IS NOT NULL - 判断是否可转为小数(含整数):
TRY_CAST(@input AS DECIMAL(18,6)) IS NOT NULL,注意精度要覆盖业务最大需求 - 必须先清理空格:
TRY_CAST(LTRIM(RTRIM(@input)) AS INT) IS NOT NULL,否则'123 '会失败 - 空字符串
''、NULL会被自然过滤掉,行为比ISNUMERIC更符合直觉
旧版本 SQL Server(2008 R2 及更早)怎么兜底
没有 TRY_CAST 就只能组合校验,但别只靠 PATINDEX('%[^0-9]%', @input) = 0 —— 它不支持小数点、负号、科学计数法,且对 '-'、'+'、'.' 无法区分合法与非法。
若只要整数(无符号):
@input IS NOT NULL AND LTRIM(RTRIM(@input)) != '' AND PATINDEX('%[^0-9]%', LTRIM(RTRIM(@input))) = 0
若要支持带符号整数或简单小数,就得加更多条件,比如:
@input NOT LIKE '%[^0-9\.\-\+]%' AND @input NOT LIKE '%[\.][\.]%' AND @input NOT LIKE '%[+\-][+\-]%' AND @input NOT LIKE '%[+\-]%' OR @input LIKE '[+\-][0-9]%'- 但这类写法极易漏边界,性能差,且仍无法覆盖
'1e2'这类情况
真正难的不是写出一个“看起来能过测试”的表达式,而是想清楚:你到底需要哪种数字?整数?带两位小数的金额?还是允许科学计数法的原始输入?不同需求对应完全不同的判断逻辑,硬塞进一个通用函数里只会埋坑。
存储过程中校验位置和错误处理必须前置
数字校验必须放在任何 INSERT、UPDATE 或计算逻辑之前,否则事务已部分执行,回滚成本高,还可能污染中间状态。
推荐写法:
IF TRY_CAST(LTRIM(RTRIM(@amount)) AS DECIMAL(18,2)) IS NULL THROW 50000, '金额格式不合法', 1;- 别用
RAISERROR,THROW更简洁、错误号可控、客户端捕获更一致 - 所有字符串参数都先
LTRIM(RTRIM()),再判空,再判数字,三步缺一不可 - 数值范围检查(如
@amount > 0 AND @amount )要和格式校验分开写,不要混在同一个 <code>IF里
复杂点在于:同一个字段在不同业务环节可能要求不同精度、不同符号策略、不同空值容忍度。把校验逻辑写死在存储过程里,后期很难复用或调整——不如在应用层统一清洗,SQL 层只做最终落地前的强约束。











