2011-08-11 16 views
5

Estoy modificando una consulta existente para un cliente, y he encontrado un problema bastante desconcertante.Conversión SQL DateTime falla cuando no debería haber ninguna conversión

Nuestro cliente utiliza SQL Server 2008 R2 y la base de datos en cuestión brinda al usuario la posibilidad de especificar campos personalizados para una de sus tablas mediante el uso de una estructura EAV. Todos los valores almacenados en esta estructura son varchar(255), y varios de los campos están destinados a almacenar fechas. La consulta en cuestión se está modificando para usar dos de estos campos y compararlos (uno es un inicio, el otro es un final) contra la fecha actual para determinar qué fila es "actual".

El problema que tengo es que la parte de la consulta hace un CONVERT(DateTime, eav.Value) para convertir el varchar en un . Las conversiones en sí toda succedd y puedo incluir el valor como parte de la cláusula SELECT, sino que forma parte de la cuestión es que me da un error de conversión:

Conversion failed when converting date and/or time from character string. 

La verdadera sorpresa es la siguiente: si defino la base de este consulta (obtener una lista de entidades con los dos valores de campo personalizados aplanados en una sola fila) como vista y seleccionar en contra de la vista y filtrar la vista por getdate(), luego funciona correctamente, pero falla si agrego una unión a un segundo table usando uno de los campos (sin fecha) de la vista. Me doy cuenta de que esto puede ser algo difícil de seguir, así que puedo publicar una consulta de ejemplo si lo desea, pero esta pregunta ya se está haciendo un poco larga.

He intentado recrear la estructura básica en otra base de datos e incluir datos de muestra, pero la nueva base de datos se comporta como se esperaba, por lo que estoy perdido aquí.

EDITAR En caso de que sea útil, aquí está el comunicado de la vista:

create view Festival as 
select 
    e.EntityId as FestivalId, 
    e.LookupAs as FestivalName, 
    convert(Date, nvs.Value) as ActivityStart, 
    convert(Date, nve.Value) as ActivityEnd 

from tblEntity e 

left join CustomControl ccs on ccs.ShortName = 'Activity Start Date' 
left join CustomControl cce on cce.ShortName = 'Activity End Date' 
left join tblEntityNameValue nvs on nvs.CustomControlId = ccs.IdCustomControl and nvs.EntityId = e.EntityId 
left join tblEntityNameValue nve on nve.CustomControlId = cce.IdCustomControl and nve.EntityId = e.EntityId 

where e.EntityType = 'Festival' 

La consulta no es lo siguiente:

select * 

from Festival f 

join FestivalAttendeeAll fa on fa.FestivalId = f.FestivalId 

where getdate() between f.ActivityStart and f.ActivityEnd 

Sin embargo, esto funciona:

select * 

from Festival f 

where getdate() between f.ActivityStart and f.ActivityEnd 

(EntityId/FestivalId ar e int columnas)

+0

probablemente debido a la configuración regional y las fechas formateadas en EE. UU. Y no estadounidenses ... Compruebe la configuración regional ... ¿Puede publicar datos de ejemplo que muestren el comportamiento? –

+0

Quizás comience con la primera consulta que falla y publíquela. – Paparazzi

+0

@Mitch: Eso sería lo primero que pensé si respondiera también, pero estas son todas las fechas en EE. UU., Formateadas en el formato "1 de enero de 2011 a las 12:00 a.m." o "1/1/2011 a las 12:00 a.m. '. La parte confusa es que el error parece estar ligado a algo que (según mi lógica, que obviamente podría estar mal) no debería tener ningún efecto sobre si se está produciendo o no una conversión. Hubiera supuesto que si los datos en realidad eran malos, la selección de la vista generaría un error, que no es así. –

Respuesta

11

He encontrado este tipo de error anteriormente, es debido al "orden de las operaciones" realizado por el plan de ejecución.

Aparece el mensaje de error porque el plan de ejecución para su extracto (generado por el optimizador) está realizando la operación CONVERT() en filas que contienen valores de cadena que no se pueden convertir a DATETIME.

Básicamente, no tiene control sobre las filas en las que el optimizador realiza la conversión. Sabe que solo necesita esa conversión en ciertas filas, y tiene predicados (cláusulas WHERE o ON) que excluyen esas filas (limite las filas a aquellas que necesitan la conversión), pero su plan de ejecución está realizando la operación CONVERT() en filas ANTES de que las filas estén excluidas.

(Por ejemplo, el optimizador puede que elijan un hacer un recorrido de tabla, y la realización de que la conversión en cada fila, antes de que se está aplicando cualquier predicado.)

no puedo dar una respuesta específica, sin una pregunta específica y un SQL específico que genera el error.


Un enfoque simple para abordar el problema sería el uso de la función ISDATE() para comprobar si el valor de la cadena se puede convertir en una fecha.

Es decir, reemplazar:

CONVERT(DATETIME,eav.Value) 

con:

CASE WHEN ISDATE(eav.Value) > 0 THEN CONVERT(DATETIME, eav.Value) ELSE NULL END 

o:

CONVERT(DATETIME, CASE WHEN ISDATE(eav.Value) > 0 THEN eav.Value ELSE NULL END) 

Tenga en cuenta que la función ISDATE() está sujeto a algunas limitaciones importantes, como siendo afectado por las configuraciones DATEFORMAT y LANGUAGE de la sesión.


Si hay alguna otra indicación en la fila EAV, se puede usar alguna otra prueba, para llevar a cabo la conversión de forma condicional.

CASE WHEN eav.ValueIsDateTime=1 THEN CONVERT(DATETIME, eav.Value) ELSE NULL END 

El otro enfoque es que he usado para tratar de obtener un mínimo de control sobre el orden de las operaciones del optimizador, mediante vistas en línea o expresiones de tabla común, con operaciones que obligan a que el optimizador materialízalos y aplica predicados, para que eso ocurra ANTES de cualquier conversión en la consulta externa.

+0

Excelente; parece un poco obvio que esto es posible ahora que lo mencionas; aunque parezca que * debe * materializar los valores como parte de la vista, puede decidir no hacerlo. Alterar la vista para usar 'ISDATE' como parte de la proyección solucionó el problema; ¡Gracias! –

+1

+1 pero, para su último párrafo, tenga en cuenta que [este enfoque no siempre funciona] (http://sqlfiddle.com/#!3/0b4c1/1). –

Cuestiones relacionadas