Pregunta

Tengo una tabla grande con más de 1 millón de registros.Desafortunadamente, la persona que creó la tabla decidió poner las fechas en un varchar(50) campo.

Necesito hacer una comparación de fechas simple.

datediff(dd, convert(datetime, lastUpdate, 100), getDate()) < 31

Pero falla en el convert():

Conversion failed when converting datetime from character string.

Al parecer hay algo en ese campo que no le gusta, y como hay tantos registros, no puedo saberlo con solo mirarlo.¿Cómo puedo desinfectar adecuadamente todo el campo de fecha para que no falle en el convert()?Esto es lo que tengo ahora:

select count(*)
from MyTable
where
    isdate(lastUpdate) > 0
    and datediff(dd, convert(datetime, lastUpdate, 100), getDate()) < 31

@SQLMenace

No me preocupa el rendimiento en este caso.Esta será una consulta única.Cambiar la tabla a un campo de fecha y hora no es una opción.

@Jon Limjap

Intenté agregar el tercer argumento y no hay diferencia.


@SQLMenace

Lo más probable es que el problema sea cómo se almacenan los datos, sólo existen dos formatos seguros;ISO AAAAMMDD;ISO 8601 aaaa-mm-dd Thh:mm:ss:mmm (sin espacios)

¿No sería el isdate() revisa ¿te encargas de esto?

No necesito una precisión del 100%.Solo quiero obtener la mayoría de los registros de los últimos 30 días.


@SQLMenace

select isdate('20080131') -- returns 1
select isdate('01312008') -- returns 0

@Brian Schkerke

Coloque CASE e ISDATE dentro de la función CONVERT().

¡Gracias!Eso fue todo.

¿Fue útil?

Solución

Colocar el CASE y ISDATE dentro de CONVERT() función.

SELECT COUNT(*) FROM MyTable
WHERE
    DATEDIFF(dd, CONVERT(DATETIME, CASE IsDate(lastUpdate)
        WHEN 1 THEN lastUpdate
        ELSE '12-30-1899'
    END), GetDate()) < 31

Reemplazar '12-30-1899' con la fecha predeterminada de su elección.

Otros consejos

¿Qué tal escribir un cursor para recorrer el contenido e intentar la conversión para cada entrada? Cuando ocurre un error, genera la clave principal u otros detalles de identificación para el registro del problema.No puedo pensar en una forma basada en conjuntos de hacer esto.

No está totalmente basado en configuraciones, pero si solo 3 filas de 1 millón son malas, le ahorrará mucho tiempo.

select * into BadDates
from Yourtable
where isdate(lastUpdate) = 0

select * into GoodDates
from Yourtable
where isdate(lastUpdate) = 1

entonces solo mira la tabla BadDates y soluciona eso

ISDATE() se encargaría de las filas que no estaban formateadas correctamente si realmente se ejecutara primero.Sin embargo, si observa el plan de ejecución, probablemente encontrará que el predicado DATEDIFF se aplica primero, de ahí la causa de su dolor.

Si está utilizando SQL Server Management Studio, presione CONTROL+l para ver el plan de ejecución estimado para una consulta en particular.

Recuerde, SQL no es un lenguaje de procedimientos y la lógica de cortocircuito puede funcionar, pero sólo si tiene cuidado al aplicarla.

¿Qué tal escribir un cursor para recorrer el contenido e intentar realizar la conversión para cada entrada?

Cuando ocurre un error, genera la clave principal u otros detalles de identificación para el registro del problema.

No puedo pensar en una forma basada en conjuntos de hacer esto.

Editar - Ah, sí, me olvidé de ISDATE().Definitivamente un mejor enfoque que usar un cursor.+1 a SQLMenace.

En su llamada de conversión, debe especificar un tercer parámetro de estilo, por ejemplo, el formato de las fechas y horas que se almacenan como varchar, como se especifica en este documento: CAST y CONVERTIR (T-SQL)

Imprima los registros.Entregue la copia impresa al idiota que decidió usar varchar(50) y pídale que encuentre el registro del problema.

La próxima vez tal vez vean el sentido de elegir un tipo de datos apropiado.

Lo más probable es que el problema sea cómo se almacenan los datos, solo existen dos formatos seguros

ISO AAAAMMDD

ISO 8601 aaaa-mm-dd Thh:mm:ss:mmm(sin espacios)

Estos funcionarán sin importar cuál sea su idioma.

Es posible que necesites hacer un SET DATEFORMAT YMD (o como se almacenen los datos) para que funcione

¿No se encargaría de esto la verificación isdate()?

Ejecute esto para ver qué pasa.

select isdate('20080131')
select isdate('01312008')

Estoy seguro de que cambiar la tabla/columna podría no ser una opción debido a los requisitos del sistema heredado, pero ¿ha pensado en crear una vista que tenga la lógica de conversión de fecha incorporada? Si está utilizando una versión más reciente de SQL, entonces ¿Es posible que incluso puedas utilizar una vista indexada?

Sugeriría limpiar el desorden y cambiar la columna a una fecha y hora porque hacer cosas como esta

WHERE datediff(dd, convert(datetime, lastUpdate), getDate()) < 31

No puedo usar un índice y será muchas veces más lento que si tuviera una columna de fecha y hora y la tuviera.

where lastUpdate > getDate() -31

También hay que tener en cuenta las horas y los segundos, por supuesto.

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