Эффективные замены ISNUMERIC () на SQL Server?
поэтому я просто потратил 5 часов на устранение неполадок, которые оказались не только из-за старый ненадежный ISNUMERIC
но похоже, что моя проблема появляется только тогда, когда UDF, в котором ISNUMERIC
объявлен WITH SCHEMABINDING
и вызывается в сохраненном proc (у меня есть много работы, чтобы перегнать его в тестовый случай, но моя первая потребность-заменить его чем-то надежным).
любые рекомендации по хорошей, эффективной замены ISNUMERIC()
. Очевидно там действительно должны быть вариации для int
, money
, etc., но что используют люди (предпочтительно в T-SQL, потому что в этом проекте я ограничен SQL Server, потому что это задача обработки данных SQL Server с большим объемом SQL Server)?
11 ответов
вы можете использовать функции 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 и систему wrap.Десятичный.Метод tryparse().
в зависимости от обстоятельств и характеристик проверки, я иногда использую вариант как выражение. Например:
NOT LIKE '%[^0-9]%'
обратите внимание, что этот конкретный пример является довольно наивно. Это не гарантирует, что значение допустимо для преобразования в определенный тип данных. Он также не позволяет использовать знаки +/- или десятичные точки, если они вам нужны.
другой вариант-написать расширенную хранимую процедуру на языке C, сделать ее DLL и сделать ее доступной для SQL Server.
Я не думаю, что потребуется слишком много строк кода, и это, вероятно, будет быстрее, чем писать управляемую хранимую процедуру в .NET, потому что вы не понесете дополнительных подслушанных от загрузки среды CLR.
вот лакомый кусочек информация: 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;
}
следуя маршруту .NET CLR, вы можете использовать регулярное выражение, особенно если вы ожидаете определенный диапазон чисел.
обычно, как правило, я стараюсь не допускать нетипизированные данные в базу данных, так как она лучше подходит либо для обработки на уровне приложения, либо для пакетного импорта обрабатывать его в службах SQL Integration Services, так что данные вводятся правильно с самого начала.
Мне приходилось делать это много раз в прошлом, и обычно самый быстрый способ-написать свою собственную пользовательскую функцию для проверки данных в формате, который вы ожидаете, так как большую часть времени накладные расходы на вызов выход на расширенный хранимый proc или управляемый код для простой проверки медленнее, чем просто делать это в T-SQL.
по данным Microsoft поддерживают только эффективное способ заменить функцию UDF-это написать собственную версию функции .NET.
конечно, если ваш администратор базы данных позволяет :).
Мой нет :(.
для 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.
вы когда-нибудь будете обрабатывать системы счисления вне вашего собственного (человеческого) языка, как китайский и т. д.? Если это так, я бы предложил использовать библиотека libuninum.
Как насчет реализации этих двух функций:
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
IsNumeric (), похоже, имеет проблемы с пробелами, "D", "E", знаками доллара и всевозможными другими символами. То, что мы обычно хотим, - это то, что говорит нам, будет ли приведение или преобразование успешным. Это 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
SQL 2012 и далее вы можете использовать функцию TRY_PARSE() вместо ISNUMERIC().
SELECT
TRY_PARSE('123' as int) as '123'
,TRY_PARSE('abc' as int) as 'abc'