Pregunta

Tengo una tabla con una columna varchar, y quiero encontrar valores que coinciden con un determinado número. Lo que permite decir que la columna contiene las siguientes entradas (excepto con millones de filas en la vida real):

123456789012
2345678
3456
23 45
713?2
00123456789012

Así que decido todas las filas que son numéricamente 123456789012 escribir una declaración que es como la siguiente:

SELECT * FROM MyTable WHERE CAST(MyColumn as bigint) = 123456789012

Debe devolver la primera y la última fila, pero en lugar de toda la consulta explota porque no puede convertir el "23 45" y "713? 2" a bigint.

¿Hay otra manera de hacer la conversión que devolverá NULL para valores que no se pueden convertir?

¿Fue útil?

Solución

SQL Server no garantiza cortocircuito operador booleano, consulte En SQL Server operador booleano cortocircuito . Así que toda la solución usando ISNUMERIC(...) AND CAST(...) están fundamentalmente equivocada (que pueden trabajar, pero bueno arbitrariamente pueden fallar después dependiong en el plan generado). Una mejor solución es utilizar CASE, como sugiere Thomas: CASE ISNUMERIC(...) WHEN 1 THEN CAST(...) ELSE NULL END. Pero, como señaló GBN, ISNUMERIC es notoriamente quisquillosos en la identificación de lo que significa '' numéricos y muchos casos en los que uno esperaría que retorne 0 devuelve 1. Así mezclar el caso con el gusto:

CASE WHEN MyRow NOT LIKE '%[^0-9]%' THEN CAST(MyRow as bigint) ELSE NULL END

Pero el verdadero problema es que si usted tiene millones de filas y hay que buscar en ellos como éste, que siempre terminan de extremo a extremo escanear ya que la expresión no es SARG-poder (no importa lo reescribimos eso). El verdadero problema aquí es la pureza de datos, y debe abordarse en el nivel adecuado, donde se rellena los datos. Otra cosa a considerar es si es posible crear una columna calculada persistido con esta expresión y crear un índice filtrado en el mismo que NULL elimina (es decir. No numérico). Eso sería acelerar las cosas un poco.

Otros consejos

Si está utilizando SQL Server 2012 puede utilizar los nuevos métodos 2:

Ambos métodos son equivalentes. Ellos devuelven un valor conversión al tipo de datos especificado si la conversión se realiza correctamente; de otro modo, devuelve null. La única diferencia es que se convierten es específico de SQL Server, CAST es ANSI. utilizando REPARTO hará que el código sea más portátil (aunque no estoy seguro si otros implementos de proveedor de base de datos TRY_CAST)

ISNUMERIC aceptará cadena y los valores vacíos como 1,23 o 5E-04 por lo que podrían no ser fiables.

Y usted no sabe qué cosas pedido será evaluado en lo que todavía podría fallar (SQL es declarativa, no de procedimiento, por lo que la cláusula WHERE probablemente no serán evaluadas de izquierda a derecha)

Así que:

  • desea aceptar el valor que consisten solamente de los caracteres 0-9
  • que necesita para materializar el filtro de "número" por lo que se aplica antes REPARTO

Algo así como:

SELECT 
*
FROM
   (
   SELECT TOP 2000000000 *
   FROM MyTable
   WHERE MyColumn NOT LIKE '%[^0-9]%'  --double negative rejects anything except 0-9
   ORDER BY MyColumn 
   ) foo
WHERE 
    CAST(MyColumn as bigint) = 123456789012 --applied after number check

Editar:. Rápido ejemplo que falla

CREATE TABLE #foo (bigintstring varchar(100))
INSERT #foo (bigintstring )VALUES ('1.23')
INSERT #foo (bigintstring )VALUES ('1 23')
INSERT #foo (bigintstring )VALUES ('123')

SELECT * FROM #foo
WHERE
   ISNUMERIC(bigintstring) = 1 
   AND 
   CAST(bigintstring AS bigint) = 123
SELECT * 
    FROM MyTable 
    WHERE ISNUMERIC(MyRow) = 1
        AND CAST(MyRow as float) = 123456789012

El ISNUMERIC () la función que debe darle lo que necesita.

SELECT * FROM MyTable
WHERE ISNUMERIC(MyRow) = 1
AND CAST(MyRow as bigint) = 123456789012

Y para añadir una declaración de caso como Thomas sugirió:

SELECT * FROM MyTable
WHERE CASE(ISNUMERIC(MyRow)
        WHEN 1 THEN CAST(MyRow as bigint)
        ELSE NULL
      END = 123456789012

http://msdn.microsoft.com/en-us/library/ ms186272.aspx

SELECT *
FROM MyTable
WHERE (ISNUMERIC(MyColumn) = 1) AND (CAST(MyColumn as bigint) = 123456789012)

Además se puede utilizar una instrucción CASE con el fin de obtener los valores nulos.

SELECT 
    CASE
        WHEN (ISNUMERIC(MyColumn) = 1) THEN CAST(MyColumn as bigint)
        ELSE NULL
    END AS 'MyColumnAsBigInt'
FROM tableName

Si usted requiere un filtrado adicional, por lo que los valores numéricos no son válidos para ser echada a bigint, puede utilizar el siguiente lugar de ISNUMERIC:

PATINDEX ( '% [^ 0-9]%', MiColumna)) = 0

Si necesita valores decimales en lugar de números enteros, fundido a flote lugar y cambiar la expresión regular a '% [^ 0-9.]%'

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top