SQL2000 安全地将 VARCHAR(256) 转换为 INT
我在 SQL2000 上将 varchar
安全地转换为 int
时遇到一些问题。
我的问题的第 1 部分是 IsNumeric 返回如果您仅查找整数,则误报。我知道为什么 IsNumeric 会这样做(浮点数、金钱等也是数字),所以我在谷歌上寻找 IsInteger
函数。
我发现以下用户定义函数(UDF):
CREATE FUNCTION dbo.IsInteger
(
@num VARCHAR(64)
)
RETURNS BIT
BEGIN
IF LEFT(@num, 1) = '-'
SET @num = SUBSTRING(@num, 2, LEN(@num))
RETURN CASE
WHEN PATINDEX('%[^0-9-]%', @num) = 0
AND CHARINDEX('-', @num) <= 1
AND @num NOT IN ('.', '-', '+', '^')
AND LEN(@num)>0
AND @num NOT LIKE '%-%'
THEN
1
ELSE
0
END
END
这似乎在检查整数方面做得很好:
declare @num varchar(256);
declare @num2 varchar(256);
set @num = '22312311';
set @num2 = '22312311.0';
SELECT @num AS [character],
dbo.IsInteger(@num) AS [isInteger],
CASE dbo.IsInteger(@num)WHEN 1 THEN convert(int, @num) ELSE NULL END AS [integer]
UNION
SELECT @num2 AS [character],
dbo.IsInteger(@num2) AS [isInteger],
CASE dbo.IsInteger(@num2)WHEN 1 THEN convert(int, @num2) ELSE NULL END AS [integer];
但是它不会验证整数是否在范围内(-2^31 <=> 2^31 - 1
)
declare @num varchar(256);
set @num = '2147483648';
SELECT @num AS [character],
dbo.IsInteger(@num) AS [isInteger],
CASE dbo.IsInteger(@num)WHEN 1 THEN convert(int, @num) ELSE NULL END AS [integer];
抛出
服务器:Msg 248,Level 16,State 1,Line 3
nvarchar 值“2147483648”的转换溢出了 int 列。超出最大整数值。
SQL2000 没有 TRY/ CATCH (答案假定 ISNUMERIC() 返回不误报)并且转换错误会导致整个批次失败,即使在 UDF 内,根据 此网站:
当UDF发生错误时, 函数的执行被中止 立即,查询也是如此,并且 除非错误是导致中止的错误 批处理,继续执行 下一条语句 – 但 @@error 是 0!
即使他们没有这样做,仍然会掩盖@@error
。我也无法转换为 bigint,因为它仍然可能崩溃(尽管不那么频繁),并且此查询是 UNION 的一部分,该 UNION 输出到 XML,并由 VB6 使用 XSLT 进行进一步验证和转换COM DLL 并显示在 2001 年编码的网站上,所以我真的(不真的)不想更改查询输出!
因此,这让我陷入了这个看似简单的任务:
如果 varchar 可转换为 int 则转换为 int 否则给我 NULL
任何指针/解决方案都会非常感激,但请注意,我不能,在没有根据情况,更改源列的数据类型,也不更改输入数据时的验证。
I'm having some problem safely casting a varchar
to int
on SQL2000.
Part 1 of my problem was that IsNumeric returns false positives if your looking for integers only. I'm aware though why IsNumeric does this though (floats, money etcetera are numeric too) so i looked for an IsInteger
function on google.
I found the following User Defined Function (UDF):
CREATE FUNCTION dbo.IsInteger
(
@num VARCHAR(64)
)
RETURNS BIT
BEGIN
IF LEFT(@num, 1) = '-'
SET @num = SUBSTRING(@num, 2, LEN(@num))
RETURN CASE
WHEN PATINDEX('%[^0-9-]%', @num) = 0
AND CHARINDEX('-', @num) <= 1
AND @num NOT IN ('.', '-', '+', '^')
AND LEN(@num)>0
AND @num NOT LIKE '%-%'
THEN
1
ELSE
0
END
END
this seems to do a good job checking for integers:
declare @num varchar(256);
declare @num2 varchar(256);
set @num = '22312311';
set @num2 = '22312311.0';
SELECT @num AS [character],
dbo.IsInteger(@num) AS [isInteger],
CASE dbo.IsInteger(@num)WHEN 1 THEN convert(int, @num) ELSE NULL END AS [integer]
UNION
SELECT @num2 AS [character],
dbo.IsInteger(@num2) AS [isInteger],
CASE dbo.IsInteger(@num2)WHEN 1 THEN convert(int, @num2) ELSE NULL END AS [integer];
However it won't validate if the integer is within range (-2^31 <=> 2^31 - 1
)
declare @num varchar(256);
set @num = '2147483648';
SELECT @num AS [character],
dbo.IsInteger(@num) AS [isInteger],
CASE dbo.IsInteger(@num)WHEN 1 THEN convert(int, @num) ELSE NULL END AS [integer];
Which throws
Server: Msg 248, Level 16, State 1, Line 3
The conversion of the nvarchar value '2147483648' overflowed an int column. Maximum integer value exceeded.
SQL2000 doesn't have TRY/CATCH (answer presumes ISNUMERIC() returns no false positives) and casting errors cause the entire batch to fail even within UDF's according to this website:
When an error occurs in a UDF,
execution of the function is aborted
immediately and so is the query, and
unless the error is one that aborts
the batch, execution continues on the
next statement – but @@error is 0!
and even if they didn't would still obscure @@error
. I also can't cast to bigint
since it might still crash (albeit not as often) and this query is part of a UNION which is output to XML which is further validated and transformed with XSLT by a VB6 COM DLL and displayed on a website coded back in 2001 so I really (no really) do not want to change the query output!.
So this leaves me stuck on this seemingly easy task:
if varchar is castable to int cast to int otherwise give me NULL
Any pointers / solutions would be much apreciated but please note that I can't, under no circumstance, change the source column's datatype nor change the validation when data is entered.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
编辑:
SQL Server 中的数字不能超过 decimal(38,0)< /a> (+/- 10^38 -1) 因此无法捕获或转换它们。这意味着长度可以为 37 个字符,并且 CAST 为十进制(38,0)
尊重 本文介绍了 .0e0 技巧
编辑OP
这个问题引导我了解以下更新的 IsInteger 函数。
Edit:
You can not have numbers over decimal(38,0) in SQL Server (+/- 10^38 -1) so can not trap them or convert them. Which means 37 characters may length and a CAST to decimal(38,0)
Respect to this article for the .0e0 trick
EDIT OP
This question lead me to the folowing updated IsInteger function.
您可以在该函数中添加更多检查:
我没有机会测试,但我认为它应该有效 - 我已将其尽可能详细地保留
You could just add a couple more checks into the function:
I've not had a chance to test but I think it should work - I've left it as verbose as possible