TSQL - 更好的 INT 转换函数
Posted
技术标签:
【中文标题】TSQL - 更好的 INT 转换函数【英文标题】:TSQL - A Better INT Conversion Function 【发布时间】:2012-06-23 22:24:15 【问题描述】:我想知道在 TSQL / SQL Server 中是否有更好的方法将 Varchar 解析为 Int。我说“解析”是因为我需要比 CAST/CONVERT 系统功能更强大的东西;当解析失败时返回 NULL 甚至是“默认”值特别有用。
所以这是我现在使用的函数,最初是从某人的 SQL 博客中获得的(甚至不记得具体是谁)...
更改功能 [dbo].[udf_ToNumber] ( @Str varchar(最大值) ) 返回整数 作为 开始 声明@Result int SET @Str = LTRIM(RTRIM(@Str)) IF (@Str='' 或 @Str 为空 或 ISNUMERIC(@Str)=0 或 @Str LIKE '%[^-+ 0-9]%' 或 @Str IN ('.', '-', '+', '^') ) SET @Result = NULL 别的 IF (CAST(@Str AS NUMERIC(38,0)) 不在 -2147483648 和 2147483647 之间。) SET @Result = NULL 别的 SET @Result = CAST(@Str AS int) 返回@结果 结尾(你可以在末尾添加一行,例如“如果@Result 为空,则设置@Result =”或类似的内容)。
这不是很有效,因为在 JOIN 或 WHERE-IN-SELECT 中使用它——比如 LEFT 列是 INT,RIGHT 是 VARCHAR,我尝试解析 RIGHT——在任何非常大的数据上——设置,比我先将 LEFT (INT) 列转换为 VARCHAR 然后执行 JOIN 花费的时间要长得多。
无论如何,我“理想地”知道,如果我的表/数据类型被适当地创建和填充,我一开始就不需要做这种事情,但我们都知道理想世界离理想世界还很远有时现实,所以请幽默。谢谢!
编辑:SQL Server 2005 和 2008 版;运行 2005 的盒子将很快升级,因此针对 2008 的答案很好。
【问题讨论】:
SQL Server 2012 会有TRY_CONVERT
和 TRY_PARSE
这样的东西 - 但在此之前,你必须自己动手(例如在 C# 中使用 SQL- CLR 设施)
也许您可以将数值存储在一个持久的计算列中,这样转换只发生在插入或更新时?
我查找了“持久计算列”,我确实看到了其中的值;但是在我的情况下,具有“违规”值的表已经存在并且包含数百万行。因此,如果计算列的“公式”相同,那么添加这样的列可能仍需要很长时间来填充其值。除非我错了——SQL Server 在填充持久计算列时比添加真实列并运行 UPDATE 查询来填充它时效率更高吗?
【参考方案1】:
根据我的经验,标量 udf 在较大的数据集上表现不佳;作为一种解决方法,您可以尝试两个选项之一(我不确定它们中的任何一个是否会特别好):
将函数的逻辑嵌入到join本身中,如下所示:
SELECT columnlist
FROM a JOIN b ON a.INT = (SELECT CASE WHEN ( b.varchar= ''
OR b.varchar IS NULL
OR ISNUMERIC(b.varchar) = 0
OR b.varchar LIKE '%[^-+ 0-9]%'
OR b.varchar IN ( '.', '-', '+', '^' )
) THEN NULL
WHEN CAST(b.varchar AS NUMERIC(38, 0)) NOT BETWEEN -2147483648.
AND 2147483647.
THEN NULL
ELSE CAST (b.varchar AS INT)
END)
将您的用户定义函数更改为内联表值函数并使用 CROSS APPLY 语法:
CREATE FUNCTION udf_ToInt
(
@str VARCHAR(MAX)
)
RETURNS TABLE
AS
RETURN
(
SELECT CASE WHEN ( @Str = ''
OR @Str IS NULL
OR ISNUMERIC(@Str) = 0
OR @Str LIKE '%[^-+ 0-9]%'
OR @Str IN ( '.', '-', '+', '^' )
) THEN NULL
WHEN CAST(@Str AS NUMERIC(38, 0)) NOT BETWEEN -2147483648.
AND 2147483647.
THEN NULL
ELSE CAST (@Str AS INT) as IntVal
END
)
GO
SELECT columnlist
FROM b
CROSS APPLY udf_ToInt(b.varchar) t
JOIN a ON t.IntVal = a.Int
转换为 VARCHAR 并进行比较可能更容易:)
【讨论】:
我为格式化道歉;由于某种原因,代码示例功能对我不起作用。 +1 以获得好主意。我用 Cross-Apply 试用了 TVF。我的查询仍然需要超过 5 分钟才能运行,但这比 20 多分钟或“永远”要好。谢谢!以上是关于TSQL - 更好的 INT 转换函数的主要内容,如果未能解决你的问题,请参考以下文章