Pregunta

Así que pasé 5 horas resolviendo un problema que resultó ser debido no solo a antiguo no confiable ISNUMERIC pero parece que mi problema solo aparece cuando el UDF en el que < code> ISNUMERIC se declara WITH SCHEMABINDING y se llama dentro de un proceso almacenado (tengo mucho trabajo por hacer para resumirlo en un caso de prueba, pero mi primera necesidad es reemplazarlo con algo confiable).

Cualquier recomendación sobre reemplazos buenos y eficientes para ISNUMERIC () . Obviamente, realmente debe haber variaciones para int , money , etc., pero qué están usando las personas (preferiblemente en T-SQL, porque en este proyecto, estoy restringido a SQL Server porque esta es una tarea de procesamiento de datos de gran volumen de SQL Server a SQL Server)?

¿Fue útil?

Solución

Puede usar las funciones T-SQL TRY_CAST () o TRY_CONVERT () si está ejecutando SQL Server 2012 como Bacon Bits menciona en los comentarios:

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

Si está utilizando SQL 2008 R2 o anterior, tendrá que usar una función .NET CLR y ajustar System.Decimal. TryParse ().

Otros consejos

Dependiendo de las circunstancias y las características de rendimiento de la validación, a veces uso una variación de la expresión LIKE en su lugar. Por ejemplo:

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

Tenga en cuenta que este ejemplo específico es bastante ingenuo. No garantiza que el valor sea válido para la conversión a un tipo de datos en particular. Tampoco permite signos +/- o puntos decimales si los necesita.

Otra opción podría ser escribir un procedimiento almacenado extendido en un lenguaje como C, convertirlo en un archivo DLL y ponerlo a disposición de SQL Server.

No me imagino que se necesitarían demasiadas líneas de código para hacerlo, y probablemente sería más rápido que escribir un Procedimiento almacenado administrado en .NET, porque no incurriría en el sobreesfuerzo adicional al cargar el CLR.

Aquí hay un dato de información: http://msdn.microsoft.com/en-us/library/ms175200. aspx

Aquí hay un código C ++ que podría funcionar para usted:

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

Siguiendo la ruta .NET CLR, puede usar una expresión regular, especialmente si espera un cierto rango de números.

SQL 2005 y expresiones regulares

Generalmente, como práctica, trato de no permitir que los datos sin tipo ingresen a la base de datos, ya que es más adecuado para manejarlos en la capa de aplicación o para las importaciones por lotes manejarlos en SQL Integration Services para que los datos entren escrito correctamente desde el principio.

He tenido que hacerlo muchas veces en el pasado y, por lo general, la forma más rápida es escribir su propia función definida por el usuario para verificar que los datos estén en el formato que espera, ya que la mayoría de las veces la sobrecarga para llamar a un El proceso almacenado extendido o el código administrado para una validación simple es más lento que simplemente hacerlo en T-SQL.

Según el soporte de Microsoft, la única forma eficiente de reemplazar la función UDF es escribir su propia versión de la función .NET.

Por supuesto, si el administrador de su base de datos lo permite :).

El mío no :(.

Para SQL Server 2005 y superior ... aproveche 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

imprime 0.

Cambia @test = '01234' o @test = '01234.5' e imprime 1.

¿Alguna vez manejará sistemas de números fuera de su propio idioma (humano), como el chino, etc.? Si es así, sugiero usar la biblioteca libuninum .

¿Qué tal implementar estas dos funciones?

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

Fuente original

IsNumeric () parece tener problemas con espacios, 'D', 'E', signos de dólar y todo tipo de otros caracteres. Lo que normalmente queremos es algo que nos diga si CAST o CONVERT tendrán éxito. Este UDF, aunque no es la solución más rápida, me ha funcionado muy bien.

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 en adelante, puede usar la función TRY_PARSE () en lugar de ISNUMERIC ().

SELECT
 TRY_PARSE('123' as int) as '123'
,TRY_PARSE('abc' as int) as 'abc'
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top