Эффективные замены ISNUMERIC () на SQL Server? - PullRequest
17 голосов
/ 23 ноября 2008

Таким образом, я потратил 5 часов на устранение проблемы, которая, как оказалось, была вызвана не только старой ненадежной ISNUMERIC, но похоже, что моя проблема появляется только тогда, когда UDF, в котором ISNUMERIC объявляется WITH SCHEMABINDING и вызывается внутри хранимого процесса (мне нужно проделать большую работу, чтобы перевести его в тестовый пример, но моя первая необходимость - заменить его чем-нибудь надежным).

Любые рекомендации по хорошей, эффективной замене ISNUMERIC(). Очевидно, что действительно должны быть варианты для int, money и т. Д., Но что люди используют (предпочтительно в T-SQL, потому что в этом проекте я ограничен SQL Server, потому что это большой объем Задача обработки данных с SQL Server на SQL Server)?

Ответы [ 11 ]

21 голосов
/ 23 ноября 2008

Вы можете использовать функции T-SQL TRY_CAST () или TRY_CONVERT (), если вы используете SQL Server 2012, как упоминает Бит Бэкон в комментариях:

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 ().

16 голосов
/ 23 ноября 2008

В зависимости от обстоятельств и характеристик производительности проверки, я иногда вместо этого использую вариант выражения LIKE. Например:

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

Обратите внимание, что этот конкретный пример довольно наивный. Это не гарантирует, что значение действительно для преобразования в определенный тип данных. Он также не учитывает знаки +/- или десятичные точки, если они вам нужны.

6 голосов
/ 27 января 2009

Другой вариант может состоять в том, чтобы написать расширенную хранимую процедуру на языке, таком как C, превратить ее в DLL и сделать ее доступной для SQL Server.

Не думаю, что на это потребуется слишком много строк кода, и, вероятно, это будет быстрее, чем написание управляемой хранимой процедуры в .NET, потому что вы не будете подвергаться дополнительной нагрузке при загрузке CLR. 1003 *

Вот немного информации: http://msdn.microsoft.com/en-us/library/ms175200.aspx

Вот код 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;
}
3 голосов
/ 27 января 2009

Следуя маршруту .NET CLR, вы можете использовать регулярное выражение, особенно если вы ожидаете определенный диапазон чисел.

SQL 2005 и регулярные выражения

2 голосов
/ 23 ноября 2008

Как правило, я стараюсь не пускать нетипизированные данные в базу данных, так как они лучше подходят для обработки на уровне приложения или для пакетного импорта для обработки в службах SQL Integration Services, чтобы данные поступали набран правильно с самого начала.

В прошлом мне приходилось делать это много раз, и обычно самый быстрый способ - написать собственную пользовательскую функцию для проверки того, что данные находятся в том формате, который вы ожидаете, поскольку в большинстве случаев накладные расходы вызываются расширенный хранимый процесс или управляемый код для простой проверки медленнее, чем просто делать это в T-SQL

1 голос
/ 24 марта 2010

Для 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.

1 голос
/ 23 ноября 2008

Согласно поддержке Microsoft, единственный эффективный способ заменить функцию UDF - это написать собственную версию функции .NET.

Конечно, если администратор вашей базы данных позволяет это:).

Шахта не: (.

0 голосов
/ 07 октября 2015

SQL 2012 и выше, вы можете использовать функцию TRY_PARSE () вместо ISNUMERIC ().

SELECT
 TRY_PARSE('123' as int) as '123'
,TRY_PARSE('abc' as int) as 'abc'
0 голосов
/ 17 октября 2009

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
0 голосов
/ 30 января 2009

Как насчет реализации этих двух функций:

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

Оригинальный источник

...