La conversión de un tipo de datos varchar a un tipo de datos datetime dio como resultado un valor fuera de rango

8

Estoy tratando de ejecutar una consulta simple para obtener todas las filas creadas en noviembre:

SELECT COUNT(*)
FROM dbo.profile 
WHERE [Created] BETWEEN '2014-11-01 00:00:00.000' 
AND '2014-11-30 23:59:59.997';

SMSS devuelve:

La conversión de un tipo de datos varchar a un tipo de datos datetime dio como resultado un valor fuera de rango.

No entiendo por qué los datos se convierten de varchar a datetime cuando 'Creado' se establece en datetime:

Columnas ¿Debo decirle al servidor que 'Creado' es fecha y hora? Si no, ¿por qué recibo este mensaje varchar?

Editar: el valor en la base de datos era YYYY-MM-DD. La respuesta de @SqlZim a continuación dice que necesito usar convert () para decirle a sql qué formato tiene la fecha en la base de datos, y reemplazar el carácter de espacio con la letra T:

select count(*) 
from dbo.profile 
where [created] between convert(datetime,'2014-11-01T00:00:00.000') 
and convert(datetime,'2014-11-30T23:59:59.997');`
jedluddley
fuente

Respuestas:

8

Revisé tu perfil y vi que estás en el Reino Unido. Si su servidor SQL está configurado para usar el formato de fecha dmy, entonces eso explica su problema. Sin utilizar la 'T' en lugar del espacio en la cadena de fecha y hora, Sql Server no lo reconocerá como formato ISO8601.

Prueba esto:

select count(*) 
  from dbo.profile 
  where [created] between convert(datetime,'2014-11-01T00:00:00.000') 
                      and convert(datetime,'2014-11-30T23:59:59.997');

Consultar usando fechas y / o horas puede ser complicado, para asegurarse de que está obteniendo lo que está buscando, le recomiendo leer:

editar: para aclarar el valor fuera de rango en su mensaje de error sería interpretar el mes como 30 y el día como 11.

SqlZim
fuente
8

No entiendo por qué los datos se están convirtiendo de varchar a datetime cuando 'Creado' se establece en datetime

Los literales que proporciona para comparar con la Createdcolumna son cadenas. Para comparar esos literales con la datetimecolumna, SQL Server intenta convertir las cadenas en datetimetipos, de acuerdo con las reglas de precedencia de tipo de datos . Sin información explícita sobre el formato de las cadenas, SQL Server sigue sus reglas complicadas para interpretar cadenas como fechas y horas.

En mi opinión, la mejor manera de evitar este tipo de problemas es ser explícito sobre los tipos. SQL Server proporciona las CAST and CONVERTfunciones para este propósito. Cuando se trabaja con cadenas y tipos de fecha / hora, CONVERTes preferible porque proporciona un parámetro de estilo para definir explícitamente el formato de cadena.

La pregunta utiliza cadenas en formato canónico ODBC (con milisegundos) (estilo 121). Ser explícito sobre el tipo de datos y el estilo de cadena da como resultado lo siguiente:

SELECT COUNT(*)
FROM dbo.profile 
WHERE [Created] BETWEEN 
    CONVERT(datetime, '2014-11-01 00:00:00.000', 121)
    AND 
    CONVERT(datetime, '2014-11-30 23:59:59.997', 121);

Dicho esto, hay buenas razones (como Aaron señala en su respuesta ) para usar un rango medio abierto en lugar de BETWEEN(uso el estilo 120 a continuación solo por variedad):

SELECT COUNT(*)
FROM dbo.profile 
WHERE
    [Created] >= CONVERT(datetime, '2014-11-01 00:00:00', 120)
    AND [Created] < CONVERT(datetime, '2014-12-01 00:00:00', 120);

Ser explícito acerca de los tipos es un hábito muy bueno para entrar, particularmente cuando se trata de fechas y horas.

Paul White 9
fuente
3

Otra alternativa, recomiendo usar literales de fecha y hora ODBC . A pesar de su nombre, no requieren que te conectes a través de ODBC. Omiten las reglas de conversión habituales en SQL Server y siempre se interpretan como a datetime.

SELECT COUNT(*)
FROM dbo.profile 
WHERE [Created] BETWEEN 
    {TS '2014-11-01 00:00:00.000'}
    AND 
    {TS '2014-11-30 23:59:59.997'};


Los otros datetimeliterales ODBC compatibles son Dy Tcomo se documenta aquí en Books Online. Ambos devuelven datetime(no dateo time), pero la sintaxis sigue siendo compacta e inequívoca. Los formatos fijos para las cadenas son:

Formatos de cadena ODBC

Ejemplo:

SELECT TOP (1)
    D = {D '2014-12-27'},
    T = {T '14:49:23.789'},
    TS = {TS '2014-12-27 14:49:23.789'};

La Tvariante devuelve el tiempo especificado en el día actual , según lo informado por el uso interno solamente {fn getdateODBC()}:

Plan de ejecución

Michael B
fuente
1
Bueno, probablemente lo haría CONVERT(DATE, '20141201')si tu necesidad de ser explícito anulara todo lo demás. Por otra parte, si la columna subyacente es un tipo de fecha / hora, esto no es realmente necesario. ¿Dices WHERE Active = CONVERT(BIT, 1)para evitar WHERE Active = 1ser interpretado como un INT?
Aaron Bertrand
3
@AaronBertrand En realidad, se sabe que hago exactamente eso :) Y aquí hay un ejemplo de por qué .
Paul White 9
-1

el siguiente código, obtiene la sesión actual dateformat, obtiene un error cuando se convierte a datetime, luego establece el formato de fecha en ymd y por último, pero no menos importante, prueba la conversión (conversión) nuevamente y funciona

-- set the dateformat for the current session
-- if you use this date format you get the following error message:
--Msg 242, Level 16, State 3, Line 9
--The conversion of a varchar data type to a datetime data type resulted in an out-of-range value.
set dateformat dmy


-- set the dateformat for the current session
--this one does not give an error message
set dateformat ymd

-- The conversion of a varchar data type 
-- to a datetime data type resulted in an out-of-range value.
select cast('2017-08-13 16:31:31'  as datetime)

-- get the current session date_format
select date_format
from sys.dm_exec_sessions
where session_id = @@spid

-- set the dateformat for the current session
set dateformat ymd

-- this should work
select cast('2017-08-13 16:31:31'  as datetime)



select @@version

Microsoft SQL Server 2016 (SP1) (KB3182545) - 13.0.4001.0 (X64) 28 de octubre de 2016 18:17:30 Copyright (c) Microsoft Corporation Enterprise Edition: Licencias basadas en Core (64 bits) en Windows Server 2012 R2 Datacenter 6.3 (Compilación 9600:) (Hipervisor)

Marcello Miorelli
fuente
¿importa explicar el voto negativo ?, ¡el código funciona bien aquí!
Marcello Miorelli