try_convert try_cast try recognized not name convert cast built sql sql-server sql-server-2008 sql-server-2012

try_cast - Try_Convert para SQL Server 2008 R2



try convert sql server 2008 (7)

Estoy usando SQL Server 2008 R2 y tengo una columna VARCHAR que quiero convertir a DECIMAL(28,10) usando CONVERT . Pero muchas de esas filas están mal formateadas, por lo que no es posible analizarlas en un número. En ese caso, solo quiero omitir esos al establecer el resultado en 0 o NULO.

Sé que hay una nueva declaración en SQL Server 2012 ( TRY_CONVERT() ) que sería útil.

¿Es esto posible en 2008 o debo esperar hasta que actualicemos a la próxima versión de SQL 2012?

EDITAR

Desafortunadamente ISNUMERIC() no es confiable en este caso. Lo intenté

ISNUMERIC(myCol) = 1

Esto devuelve verdadero para las filas que CONVERT no puede convertir a DECIMAL .


Al usar XML en SQL Server puede tratar de convertir a un tipo de datos y recibir valores nulos donde falla el lanzamiento.

declare @T table ( Col varchar(50) ) insert into @T values (''1''), (''1.1''), (''1,1''), (''1a'') select cast('''' as xml).value(''sql:column("Col") cast as xs:decimal ?'', ''decimal(28,10)'') as Col from @T

Resultado:

Col ------------- 1.0000000000 1.1000000000 NULL NULL


Como se trata de un cambio permanente, lo haría como un proceso de dos pasos: primero, elimine el texto no válido y luego convierta la columna.

Para eliminar el texto no válido, haría algo como:

UPDATE [Table] SET [Column] = NULL WHERE [Column] LIKE ''%[^0-9.]%'' or LEN([Column]) - LEN(REPLACE([Column],''.'','''')) > 1 or LEN([Column]) > 28

Una vez hecho esto, todo lo que queda debe ser convertible simplemente cambiando la definición de columna

ALTER TABLE [Table] ALTER COLUMN [Column] decimal(28,10)


Escribí una función escalar útil para simular la función TRY_CAST de SQL Server 2012 en SQL Server 2008.

dbo.TRY_CAST(Expression, Data_Type, ReturnValueIfErrorCast)

Las dos principales diferencias con TRY_CAST Function para SQL Server 2012 son que debe pasar 3 parámetros y además debe realizar un CONVERT o CAST explícito en el campo. Sin embargo, sigue siendo muy útil porque le permite devolver un valor predeterminado si CAST no se realiza correctamente.

CÓDIGO DE FUNCIÓN:

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 ------------------------------------------------------------------------------------------------------------------------ Update by: Javier Eduardo Pardo Moreno Date: febrero 16/2017 Id update: JEPM20170216 Description: Fix ISNUMERIC function makes it unreliable. SELECT dbo.TRY_CAST(''+'', ''INT'', 0) will yield Msg 8114, Level 16, State 5, Line 16 Error converting data type varchar to float. ISNUMERIC() function treats few more characters as numeric, like: – (minus), + (plus), $ (dollar), / (back slash), (.)dot and (,)comma Collaborator aperiooculus (http://.com/users/3083382/aperiooculus ) Fix dbo.TRY_CAST(''2013/09/20'', ''datetime'', DEFAULT) for supporting DATETIME format 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 AND @pExpression NOT IN (''-'',''+'',''$'',''.'','','',''/') --JEPM20170216 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 IN (''DATE'',''DATETIME'') BEGIN IF ISDATE(@pExpression) = 1 BEGIN DECLARE @pExpressionDATE AS DATETIME = cast(@pExpression AS DATETIME) IF @pData_Type = ''DATE'' BEGIN RETURN cast(@pExpressionDATE as DATE) END IF @pData_Type = ''DATETIME'' BEGIN RETURN cast(@pExpressionDATE as DATETIME) END END ELSE BEGIN DECLARE @pExpressionDATEReplaced AS VARCHAR(50) = REPLACE(REPLACE(REPLACE(@pExpression,''/',''''),''/'',''''),''-'','''') IF ISDATE(@pExpressionDATEReplaced) = 1 BEGIN IF @pData_Type = ''DATE'' BEGIN RETURN cast(@pExpressionDATEReplaced as DATE) END IF @pData_Type = ''DATETIME'' BEGIN RETURN cast(@pExpressionDATEReplaced as DATETIME) END END ELSE BEGIN RETURN @pReturnValueIfErrorCast END 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

Por ahora, solo admite los tipos de datos INT, DATE, DATETIME, NUMERIC, BIT y FLOAT . Puede encontrar la última versión de este código en el siguiente enlace a continuación y nos ayudamos mutuamente para mejorarlo. Función TRY_CAST para SQL Server 2008 https://gist.github.com/jotapardo/800881eba8c5072eb8d99ce6eb74c8bb


Finalmente descubrí cómo hacerlo con la ayuda de SO y Google.

La declaración de actualización:

UPDATE PriceTerm SET PercentAddition = CONVERT(decimal(28,10), RTRIM(LTRIM(REPLACE(REPLACE(REPLACE(AdditionalDescription,''%'',''''), '','',''.''), ''&'', '''')))) WHERE AdditionalDescription LIKE ''%[%]%'' AND dbo.isreallynumeric(RTRIM(LTRIM(REPLACE(REPLACE(REPLACE(AdditionalDescription,''%'',''''), '','',''.''), ''&'', '''')))) = 1 AND PercentAddition = 0

Primero busco% char como la mayoría de las veces que se utiliza como marcador para el valor porcentual. Pero también hay otros usos al azar. Resultó que ISNUMERIC no era confiable en mi caso.

Lo que realmente hace la diferencia es que la llamada al procedimiento almacenado es realinemónica desde here .

Asi que

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


Prefiero soluciones simples e hice un SP como el siguiente.

CREATE PROC TRY_CAST ( @valueToBeParsed VARCHAR(64), @parsedValue INT OUTPUT ) AS BEGIN BEGIN TRY SELECT @parsedValue = cast(@valueToBeParsed as int) END TRY BEGIN CATCH SET @parsedValue = null END CATCH END GO

y lo usé como siguiendo

DECLARE @val int EXEC TRY_CAST ''1w'', @val out select @val


Puede escribir su propio analizador personalizado en C # y usar SQLCLR, usando, por ejemplo, Decimal.Parse() . No intente utilizar ISNUMERIC , es notoriamente incorrect (devuelve TRUE para las cadenas que no pueden CAST).


SQL SERVER 2008 R2 tiene una función TRY_CAST pero si configura su DB con compatibilidad con 2005, encontró que SQL SERVER no lo reconoce.

Utilicé la función que crea pero renombre TRY_CAST2 porque SS en este caso reconoció la función.