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_CONVERTTRY_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 转换函数的主要内容,如果未能解决你的问题,请参考以下文章

TSQL 数据类型转换

何时使用 trunc() 而不是 int() 将浮点类型数转换为整数更好?

使用滞后函数(TSQL)返回0获得销售差异

将 const void* 转换为 const int*

怎么把int转换为char类型

pythonint转换成c_uint