try_convert в SQL Server 2008 R2 Express

69
5

У меня есть запрос, который был написан для SQL Server 2012, и использует try_convert(). Теперь мне нужно выполнить запрос на SQL Server 2008 R2 Express, и я понимаю, что try_convert() не поддерживается в 2008 R2.

Мой текущий запрос содержит этот блок:

CASE WHEN (try_convert(decimal, tew_userdata_locTo.use_data0) IS NULL) 
THEN .. ELSE .. END

Это дает мне истинный или ложный результат, позволяющий мне знать, могу ли я преобразовать конкретное значение в десятичный. Если я смогу преобразовать значение, я сделаю это. Если нет, я использую десятичное значение по умолчанию и продолжаю с этим.

Любые идеи о том, что я мог бы написать, которые будут работать в SQL Server 2008 R2?

спросил(а) 2014-03-27T00:30:00+04:00 6 лет, 6 месяцев назад
1
Решение
57

try_convert() пытается выполнить преобразование. Это хороший способ обработки ошибок конверсии (или, по крайней мере, лучше, чем SQL Server ранее, что было ничем). Вы можете использовать, like чтобы увидеть, что строка "выглядит" как действительная десятичная. Вот одна попытка:

CASE WHEN tew_userdata_locTo.use_data0 not like '%[^0-9.]%' and
tew_userdata_locTo.use_data0 not like '%.%.%'
THEN convert(decimal, tew_userdata_locTo.use_data0)
THEN .. ELSE .. END

ответил(а) 2014-03-27T00:41:00+04:00 6 лет, 6 месяцев назад
40

Еще одно решение использует сборки CLR. Это позволяет "связывать" функции, написанные на С# или VB, которые будут работать намного быстрее, чем любой эквивалент SQL.

В следующей статье описывается, как создать сборку CLR в профессиональной версии Visual Studio. Однако это может быть создано любой другой версией (т.е. Сообществом) в виде библиотеки классов и вручную зарегистрировано в сборках базы данных.

Для текущей проблемы проверка выполняется просто следующим классом:


[Microsoft.SqlServer.Server.SqlFunction]
public static SqlBoolean IsDecimal(string value)
{
decimal decValue;
bool isNumeric = decimal.TryParse(value, out decValue);
return new SqlBoolean(isNumeric);
}

Пример использования:

select dbo.IsDecimal(1.0) --> 1
select dbo.IsDecimal(1.0a) --> 0

-- quick and dirty performance test -> takes about 2 seconds (250K records)
-- as opposed to about 1 second of just selecting the data without any filter
select * from sys.messages where dbo.IsDecimal(severity) = 1

ответил(а) 2017-01-20T12:23:00+03:00 3 года, 8 месяцев назад
40

Я написал полезную скалярную функцию для моделирования функции TRY_CAST SQL SERVER 2012 в SQL Server 2008.

dbo.TRY_CAST(Expression, Data_Type, ReturnValueIfErrorCast)

Двумя основными отличиями функции TRY_CAST для SQL Server 2012 являются то, что вы должны передать 3 параметра, и вы должны дополнительно выполнить явное CONVERT или CAST в поле. Однако он по-прежнему очень полезен, потому что он позволяет вернуть значение по умолчанию, если CAST выполняется неправильно.

КОД ФУНКЦИИ:

DECLARE @strSQL NVARCHAR(1000)
IF NOT EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[TRY_CAST]'))
BEGIN
SET @strSQL = 'CREATE FUNCTION [dbo].[TRY_CAST] () RETURNS INT AS BEGIN RETURN 0 END'
EXEC sys.sp_executesql @strSQL
END

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

/*
------------------------------------------------------------------------------------------------------------------------
Description:
Syntax
---------------
dbo.TRY_CAST(Expression, Data_Type, ReturnValueIfErrorCast)
+---------------------------+-----------------------+
| Expression | VARCHAR(8000) |
+---------------------------+-----------------------+
| Data_Type | VARCHAR(8000) |
+---------------------------+-----------------------+
| ReturnValueIfErrorCast | SQL_VARIANT = NULL |
+---------------------------+-----------------------+
Arguments
---------------
expression
The value to be cast. Any valid expression.
Data_Type
The data type into which to cast expression.
ReturnValueIfErrorCast
Value returned if cast fails or is not supported. Required. Set the DEFAULT value by default.
Return Type
----------------
Returns value cast to SQL_VARIANT type if the cast succeeds; otherwise, returns null if the parameter @pReturnValueIfErrorCast is set to DEFAULT,
or that the user indicates.
Remarks
----------------
dbo.TRY_CAST function simulates the TRY_CAST function reserved of SQL SERVER 2012 for using in SQL SERVER 2008.
dbo.TRY_CAST function takes the value passed to it and tries to convert it to the specified Data_Type.
If the cast succeeds, dbo.TRY_CAST returns the value as SQL_VARIANT type; if the cast doesn´t succees, null is returned if the parameter @pReturnValueIfErrorCast is set to DEFAULT.
If the Data_Type is unsupported will return @pReturnValueIfErrorCast.
dbo.TRY_CAST function requires user make an explicit CAST or CONVERT in ANY statements.
This version of dbo.TRY_CAST only supports CAST for INT, DATE, NUMERIC and BIT types.

Examples
====================================================================================================

--A. Test TRY_CAST function returns null
SELECT
CASE WHEN dbo.TRY_CAST('6666666166666212', 'INT', DEFAULT) IS NULL
THEN 'Cast failed'
ELSE 'Cast succeeded'
END AS Result;
GO
--B. Error Cast With User Value
SELECT
dbo.TRY_CAST('2147483648', 'INT', DEFAULT) AS [Error Cast With DEFAULT],
dbo.TRY_CAST('2147483648', 'INT', -1) AS [Error Cast With User Value],
dbo.TRY_CAST('2147483648', 'INT', NULL) AS [Error Cast With User NULL Value];
GO
--C. Additional CAST or CONVERT required in any assignment statement
DECLARE @IntegerVariable AS INT
SET @IntegerVariable = CAST(dbo.TRY_CAST(123, 'INT', DEFAULT) AS INT)
SELECT @IntegerVariable
GO
IF OBJECT_ID('tempdb..#temp') IS NOT NULL
DROP TABLE #temp
CREATE TABLE #temp (
Id INT IDENTITY
, FieldNumeric NUMERIC(3, 1)
)
INSERT INTO dbo.#temp (FieldNumeric)
SELECT CAST(dbo.TRY_CAST(12.3, 'NUMERIC(3,1)', 0) AS NUMERIC(3, 1));--Need explicit CAST on INSERT statements
SELECT *
FROM #temp
DROP TABLE #temp

GO
--D. Supports CAST for INT, DATE, NUMERIC and BIT types.
SELECT dbo.TRY_CAST(2147483648, 'INT', 0) AS [Cast failed]
, dbo.TRY_CAST(2147483647, 'INT', 0) AS [Cast succeeded]
, SQL_VARIANT_PROPERTY(dbo.TRY_CAST(212, 'INT', 0), 'BaseType') AS [BaseType];
SELECT dbo.TRY_CAST('AAAA0101', 'DATE', DEFAULT) AS [Cast failed]
, dbo.TRY_CAST('20160101', 'DATE', DEFAULT) AS [Cast succeeded]
, SQL_VARIANT_PROPERTY(dbo.TRY_CAST('2016-01-01', 'DATE', DEFAULT), 'BaseType') AS [BaseType];
SELECT dbo.TRY_CAST(1.23, 'NUMERIC(3,1)', DEFAULT) AS [Cast failed]
, dbo.TRY_CAST(12.3, 'NUMERIC(3,1)', DEFAULT) AS [Cast succeeded]
, SQL_VARIANT_PROPERTY(dbo.TRY_CAST(12.3, 'NUMERIC(3,1)', DEFAULT), 'BaseType') AS [BaseType];
SELECT dbo.TRY_CAST('A', 'BIT', DEFAULT) AS [Cast failed]
, dbo.TRY_CAST(1, 'BIT', DEFAULT) AS [Cast succeeded]
, SQL_VARIANT_PROPERTY(dbo.TRY_CAST('123', 'BIT', DEFAULT), 'BaseType') AS [BaseType];
GO
--E. B. TRY_CAST return NULL on unsupported data_types

SELECT dbo.TRY_CAST(4, 'xml', DEFAULT) AS [unsupported];

GO
====================================================================================================
Responsible: Javier Pardo
Date: diciembre 29/2016
WB tests: Javier Pardo
------------------------------------------------------------------------------------------------------------------------
*/

ALTER FUNCTION dbo.TRY_CAST
(
@pExpression AS VARCHAR(8000),
@pData_Type AS VARCHAR(8000),
@pReturnValueIfErrorCast AS SQL_VARIANT = NULL
)
RETURNS SQL_VARIANT
AS
BEGIN
--------------------------------------------------------------------------------
-- INT
--------------------------------------------------------------------------------

IF @pData_Type = 'INT'
BEGIN
IF ISNUMERIC(@pExpression) = 1
BEGIN
DECLARE @pExpressionINT AS FLOAT = CAST(@pExpression AS FLOAT)

IF @pExpressionINT BETWEEN - 2147483648.0 AND 2147483647.0
BEGIN
RETURN CAST(@pExpressionINT as INT)
END
ELSE
BEGIN
RETURN @pReturnValueIfErrorCast
END --FIN IF @pExpressionINT BETWEEN - 2147483648.0 AND 2147483647.0
END
ELSE
BEGIN
RETURN @pReturnValueIfErrorCast
END -- FIN IF ISNUMERIC(@pExpression) = 1
END -- FIN IF @pData_Type = 'INT'

--------------------------------------------------------------------------------
-- DATE
--------------------------------------------------------------------------------

IF @pData_Type = 'DATE'
BEGIN
IF ISDATE(@pExpression) = 1
BEGIN
DECLARE @pExpressionDATE AS DATE = cast(@pExpression AS DATE)

RETURN cast(@pExpressionDATE as DATE)
END
ELSE
BEGIN
RETURN @pReturnValueIfErrorCast
END --FIN IF ISDATE(@pExpression) = 1
END --FIN IF @pData_Type = 'DATE'

--------------------------------------------------------------------------------
-- NUMERIC
--------------------------------------------------------------------------------

IF @pData_Type LIKE 'NUMERIC%'
BEGIN

IF ISNUMERIC(@pExpression) = 1
BEGIN

DECLARE @TotalDigitsOfType AS INT = SUBSTRING(@pData_Type,CHARINDEX('(',@pData_Type)+1, CHARINDEX(',',@pData_Type) - CHARINDEX('(',@pData_Type) - 1)
, @TotalDecimalsOfType AS INT = SUBSTRING(@pData_Type,CHARINDEX(',',@pData_Type)+1, CHARINDEX(')',@pData_Type) - CHARINDEX(',',@pData_Type) - 1)
, @TotalDigitsOfValue AS INT
, @TotalDecimalsOfValue AS INT
, @TotalWholeDigitsOfType AS INT
, @TotalWholeDigitsOfValue AS INT

SET @pExpression = REPLACE(@pExpression, ',','.')

SET @TotalDigitsOfValue = LEN(REPLACE(@pExpression, '.',''))
SET @TotalDecimalsOfValue = CASE Charindex('.', @pExpression)
WHEN 0
THEN 0
ELSE Len(Cast(Cast(Reverse(CONVERT(VARCHAR(50), @pExpression, 128)) AS FLOAT) AS BIGINT))
END
SET @TotalWholeDigitsOfType = @TotalDigitsOfType - @TotalDecimalsOfType
SET @TotalWholeDigitsOfValue = @TotalDigitsOfValue - @TotalDecimalsOfValue

-- The total digits can not be greater than the p part of NUMERIC (p, s)
-- The total of decimals can not be greater than the part s of NUMERIC (p, s)
-- The total digits of the whole part can not be greater than the subtraction between p and s
IF (@TotalDigitsOfValue <= @TotalDigitsOfType) AND (@TotalDecimalsOfValue <= @TotalDecimalsOfType) AND (@TotalWholeDigitsOfValue <= @TotalWholeDigitsOfType)
BEGIN
DECLARE @pExpressionNUMERIC AS FLOAT
SET @pExpressionNUMERIC = CAST (ROUND(@pExpression, @TotalDecimalsOfValue) AS FLOAT)

RETURN @pExpressionNUMERIC --Returns type FLOAT
END
else
BEGIN
RETURN @pReturnValueIfErrorCast
END-- FIN IF (@TotalDigitisOfValue <= @TotalDigits) AND (@TotalDecimalsOfValue <= @TotalDecimals)

END
ELSE
BEGIN
RETURN @pReturnValueIfErrorCast
END --FIN IF ISNUMERIC(@pExpression) = 1
END --IF @pData_Type LIKE 'NUMERIC%'

--------------------------------------------------------------------------------
-- BIT
--------------------------------------------------------------------------------

IF @pData_Type LIKE 'BIT'
BEGIN
IF ISNUMERIC(@pExpression) = 1
BEGIN
RETURN CAST(@pExpression AS BIT)
END
ELSE
BEGIN
RETURN @pReturnValueIfErrorCast
END --FIN IF ISNUMERIC(@pExpression) = 1
END --IF @pData_Type LIKE 'BIT'

--------------------------------------------------------------------------------
-- FLOAT
--------------------------------------------------------------------------------

IF @pData_Type LIKE 'FLOAT'
BEGIN
IF ISNUMERIC(REPLACE(REPLACE(@pExpression, CHAR(13), ''), CHAR(10), '')) = 1
BEGIN

RETURN CAST(@pExpression AS FLOAT)
END
ELSE
BEGIN

IF REPLACE(@pExpression, CHAR(13), '') = '' --Only white spaces are replaced, not new lines
BEGIN
RETURN 0
END
ELSE
BEGIN
RETURN @pReturnValueIfErrorCast
END --IF REPLACE(@pExpression, CHAR(13), '') = ''

END --FIN IF ISNUMERIC(@pExpression) = 1
END --IF @pData_Type LIKE 'FLOAT'

--------------------------------------------------------------------------------
-- Any other unsupported data type will return NULL or the value assigned by the user to @pReturnValueIfErrorCast
--------------------------------------------------------------------------------

RETURN @pReturnValueIfErrorCast

END

Пока поддерживает только типы данных INT, DATE, NUMERIC, BIT и FLOAT. Вы можете найти последнюю версию этого кода в следующей ссылке ниже, и мы помогаем друг другу улучшать ее. Функция TRY_CAST для SQL Server 2008 https://gist.github.com/jotapardo/800881eba8c5072eb8d99ce6eb74c8bb

ответил(а) 2017-01-04T18:40:00+03:00 3 года, 8 месяцев назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема