Подтвердить что ты не робот

Эффективные замены ISNUMERIC() на SQL Server?

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

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

4b9b3361

Ответ 1

Вы можете использовать функции T-SQL TRY_CAST() или TRY_CONVERT(), если вы используете SQL Server 2012 в качестве 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().

Ответ 2

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

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

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

Ответ 3

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

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

Здесь есть лакомый кусок информации: http://msdn.microsoft.com/en-us/library/ms175200.aspx

Вот код С++, который может сработать для вас:

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;
}

Ответ 4

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

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

Ответ 5

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

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

Ответ 6

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

Конечно, если ваш администратор базы данных допускает следующее:).

Моя не делает: (.

Ответ 7

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

Ответ 8

Вы когда-нибудь будете обрабатывать числовые системы вне своего (человеческого) языка, например, китайского и т.д.? Если это так, я бы предложил использовать библиотеку libuninum.

Ответ 9

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

Ответ 10

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

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

Ответ 11

Как реализовать эти две функции:

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

Исходный источник