SQL Server 上的高效 ISNUMERIC() 替换?

发布于 2024-07-09 18:16:33 字数 557 浏览 9 评论 0原文

因此,我花了 5 个小时排除了一个问题,结果发现该问题不仅是由于 旧的不可靠 ISNUMERIC 但看起来我的问题仅在 UDF 其中 ISNUMERIC 时出现 声明为 WITH SCHEMABINDING 并在存储过程中调用(我需要做很多工作才能将其提取到测试用例中,但我的第一个需要是替换它与可靠的东西)。

有关 ISNUMERIC() 的良好、高效替代方案的任何建议。 显然,intmoney 等确实需要有所变化,但是人们使用的是什么(最好是在 T-SQL 中,因为在这个项目中,我受到限制)到 SQL Server,因为这是一个大容量 SQL Server 到 SQL Server 数据处理任务)?

So I just spent 5 hours troubleshooting a problem which turned out to be due not only to the old unreliable ISNUMERIC but it looks like my problem only appears when the UDF in which ISNUMERIC is declared WITH SCHEMABINDING and is called within a stored proc (I've got a lot of work to do to distill it down into a test case, but my first need is to replace it with something reliable).

Any recommendations on good, efficient replacements for ISNUMERIC(). Obviously there really need to be variations for int, money, etc., but what are people using (preferably in T-SQL, because on this project, I'm restricted to SQL Server because this is a high-volume SQL Server to SQL Server data processing task)?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

高速公鹿 2024-07-16 18:16:33

如果您运行的是 SQL Server 2012,则可以使用 T-SQL 函数 TRY_CAST() 或 TRY_CONVERT(),正如 Bacon Bits 在评论中提到的那样:

SELECT CASE WHEN TRY_CAST('foo' AS INT) IS NULL THEN 0 ELSE 1 END

SELECT CASE WHEN TRY_CAST(1 AS INT) IS NULL THEN 0 ELSE 1 END

如果您使用的是 SQL 2008 R2 或更早版本,则必须使用 . NET CLR 函数,并包装 System.Decimal.TryParse()。

You can use the T-SQL functions TRY_CAST() or TRY_CONVERT() if you're running SQL Server 2012 as Bacon Bits mentions in the comments:

SELECT CASE WHEN TRY_CAST('foo' AS INT) IS NULL THEN 0 ELSE 1 END

SELECT CASE WHEN TRY_CAST(1 AS INT) IS NULL THEN 0 ELSE 1 END

If you're using SQL 2008 R2 or older, you'll have to use a .NET CLR function, and wrap System.Decimal.TryParse().

讽刺将军 2024-07-16 18:16:33

根据验证的环境和性能特征,我有时会使用 LIKE 表达式的变体。 例如:

NOT LIKE '%[^0-9]%'

请注意,这个具体示例相当幼稚。 它不保证该值对于转换为特定数据类型是有效的。 如果您需要的话,它也不允许使用 +/- 符号或小数点。

Depending on the circumstances and the performance characteristics of the validation, I sometimes use a variation of the LIKE expression instead. For example:

NOT LIKE '%[^0-9]%'

Note that this specific example is fairly naive. It doesn't guarantee that the value is valid for conversion to a particular data type. It also doesn't allow for +/- signs or decimal points if you need those.

暮光沉寂 2024-07-16 18:16:33

另一种选择可能是用 C 等语言编写扩展存储过程,将其放入 DLL 中并可供 SQL Server 使用。

我不认为这需要太多代码行,而且它可能比在 .NET 中编写托管存储过程更快,因为您不会因加载 CLR 而产生额外的监听。

这里有一个信息:
http://msdn.microsoft.com/en-us/library/ms175200。下面

是一些可能适合您的 C++ 代码:

using namespace std;

int checkNumber() {
  int number = 0;
  cin >> number;
  cin.ignore(numeric_limits<int>::max(), '\n');

  if (!cin || cin.gcount() != 1)
    cout << "Not a number.";
  else
    cout << "Your entered: " << number;
  return 0;
}

Another option might be to write an extended stored procedure in a language such as C, make it into a DLL and make it available to SQL Server.

I don't imagine it would take too many lines of code to do, and it probably would be faster than writing a Managed Stored Procedure in .NET, because you wouldn't incur the extra overheard from loading the CLR.

Here's a tidbit of information:
http://msdn.microsoft.com/en-us/library/ms175200.aspx

Here's some C++ code that might work for you:

using namespace std;

int checkNumber() {
  int number = 0;
  cin >> number;
  cin.ignore(numeric_limits<int>::max(), '\n');

  if (!cin || cin.gcount() != 1)
    cout << "Not a number.";
  else
    cout << "Your entered: " << number;
  return 0;
}
黎夕旧梦 2024-07-16 18:16:33

一般来说,作为一种实践,我尽量不要让非类型化数据进入数据库,因为它更适合在应用程序层处理它,或者批量导入在 SQL Integration Services 中处理它,以便数据从正确的类型中输入开始。

过去我不得不这样做很多次,通常最快的方法是编写自己的用户定义函数来验证数据是否采用您期望的格式,因为大多数时候调用扩展存储过程的开销或用于简单验证的托管代码比仅在 T-SQL 中执行要慢。

Generally as a practice, I try not to let untyped data into the database, as it is better more suited to either handle it at the application layer, or for batch imports handle it in SQL Integration Services so that the data comes in typed correctly from the start.

I have had to do it many times in the past and usually the fastest way is to write your own user defined function to verify the data is in the format you expect, as most of the time the overhead to calling out to an extended stored proc or managed code for simple validation is slower than just doing it in T-SQL.

浅黛梨妆こ 2024-07-16 18:16:33

遵循 .NET CLR 路线,您可以使用正则表达式,特别是如果您期望特定范围的数字。

SQL 2005 和正则表达式

Following the .NET CLR route, you could go with a regular expression, particularly if you expect a certain range of numbers.

SQL 2005 and Regular Expressions

下雨或天晴 2024-07-16 18:16:33

根据 Microsoft 支持,替换 UDF 函数的唯一有效方法是编写自己的 .NET 函数版本。

当然,如果您的数据库管理员允许的话:)。

我的没有:(。

According to Microsoft support the only efficient way to replace UDF function is to write your own .NET function version.

Of course, if your database admin allows that :).

Mine doesn't :(.

歌枕肩 2024-07-16 18:16:33

您是否曾经要处理您自己(人类)语言之外的数字系统,例如中文等? 如果是这样,我建议使用 libuninum 库

Are you ever going to be handling number systems outside of your own (human) language, like Chinese etc? If so, I'd suggest using the libuninum library.

只有影子陪我不离不弃 2024-07-16 18:16:33

如何实现这两个功能:

CREATE FUNCTION dbo.isReallyNumeric  
(  
    @num VARCHAR(64)  
)  
RETURNS BIT  
BEGIN  
    IF LEFT(@num, 1) = '-'  
        SET @num = SUBSTRING(@num, 2, LEN(@num))  

    DECLARE @pos TINYINT  

    SET @pos = 1 + LEN(@num) - CHARINDEX('.', REVERSE(@num))  

    RETURN CASE  
    WHEN PATINDEX('%[^0-9.-]%', @num) = 0  
        AND @num NOT IN ('.', '-', '+', '^') 
        AND LEN(@num)>0  
        AND @num NOT LIKE '%-%' 
        AND  
        (  
            ((@pos = LEN(@num)+1)  
            OR @pos = CHARINDEX('.', @num))  
        )  
    THEN  
        1  
    ELSE  
    0  
    END  
END  
GO  

CREATE FUNCTION dbo.isReallyInteger  
(  
    @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  
GO

原始来源

How about implementing these two functions:

CREATE FUNCTION dbo.isReallyNumeric  
(  
    @num VARCHAR(64)  
)  
RETURNS BIT  
BEGIN  
    IF LEFT(@num, 1) = '-'  
        SET @num = SUBSTRING(@num, 2, LEN(@num))  

    DECLARE @pos TINYINT  

    SET @pos = 1 + LEN(@num) - CHARINDEX('.', REVERSE(@num))  

    RETURN CASE  
    WHEN PATINDEX('%[^0-9.-]%', @num) = 0  
        AND @num NOT IN ('.', '-', '+', '^') 
        AND LEN(@num)>0  
        AND @num NOT LIKE '%-%' 
        AND  
        (  
            ((@pos = LEN(@num)+1)  
            OR @pos = CHARINDEX('.', @num))  
        )  
    THEN  
        1  
    ELSE  
    0  
    END  
END  
GO  

CREATE FUNCTION dbo.isReallyInteger  
(  
    @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  
GO

Original Source

唐婉 2024-07-16 18:16:33

对于 SQL Server 2005 及更高版本...利用 try/catch...

declare @test varchar(10), @num decimal
select @test = '0123A'

begin try
    select @num = cast(@test as decimal)
    print '1'
end try 
begin catch
    print '0'
end catch

打印 0。

更改 @test = '01234' 或 @test = '01234.5' 并打印 1。

For SQL Server 2005 and above.... take advantage of try/catch...

declare @test varchar(10), @num decimal
select @test = '0123A'

begin try
    select @num = cast(@test as decimal)
    print '1'
end try 
begin catch
    print '0'
end catch

prints 0.

Change @test = '01234' or @test = '01234.5' and it prints 1.

我不吻晚风 2024-07-16 18:16:33

IsNumeric() 似乎对空格、“D”、“E”、美元符号和各种其他字符有问题。 我们通常想要的是告诉我们 CAST 或 CONVERT 是否会成功。 这个 UDF 虽然不是最快的解决方案,但对我来说效果很好。

create function dbo.udf_IsNumeric(@str varchar(50))
  returns int
as
begin
  declare @rtn int
  select @rtn =
    case
      when ltrim(rtrim(@str)) in('.', '-', '-.', '+', '+.') then 0
      when ltrim(rtrim(@str)) like '%[^-+.0-9]%' then 0
      else isnumeric(@str)
    end
  return @rtn
end

IsNumeric() seems to have problems with spaces, 'D', 'E', dollar signs and all sorts of other characters. What we typically want is something that tells us whether a CAST or CONVERT will succeed. This UDF, while not the fastest solution, has worked very well for me.

create function dbo.udf_IsNumeric(@str varchar(50))
  returns int
as
begin
  declare @rtn int
  select @rtn =
    case
      when ltrim(rtrim(@str)) in('.', '-', '-.', '+', '+.') then 0
      when ltrim(rtrim(@str)) like '%[^-+.0-9]%' then 0
      else isnumeric(@str)
    end
  return @rtn
end
何必那么矫情 2024-07-16 18:16:33

从 SQL 2012 开始,您可以使用 TRY_PARSE() 函数代替 ISNUMERIC()。

SELECT
 TRY_PARSE('123' as int) as '123'
,TRY_PARSE('abc' as int) as 'abc'

SQL 2012 onwards, you can use TRY_PARSE() function instead of ISNUMERIC().

SELECT
 TRY_PARSE('123' as int) as '123'
,TRY_PARSE('abc' as int) as 'abc'
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文