La conversione di un tipo di dati varchar in un tipo di dati datetime ha prodotto un valore fuori range


8

Sto cercando di eseguire una semplice query per ottenere tutte le righe create a novembre:

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

Resi SMSS:

La conversione di un tipo di dati varchar in un tipo di dati datetime ha prodotto un valore fuori intervallo.

Non capisco perché i dati vengano convertiti da varchar a datetime quando 'Creato' è impostato su datetime:

colonne Devo dire al server che "Creato" è datetime? In caso contrario, perché ricevo questo messaggio varchar?

Modifica: il valore nel database era YYYY-MM-DD. La risposta di @SqlZim sotto dice che devo usare convert () per dire a sql quale formato è la data nel db - e per sostituire il carattere dello spazio con la lettera 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');`

Risposte:


8

Ho controllato il tuo profilo e ho visto che sei nel Regno Unito. Se il tuo server sql è impostato per utilizzare il dateformat dmy, questo spiega il problema. Senza usare la 'T' invece dello spazio nella stringa datetime, SQL Server non lo riconoscerà come formato ISO8601.

Prova questo:

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');

Interrogare utilizzando date e / o orari può essere complicato, per assicurarti di ottenere ciò che stai cercando ti consiglio di leggere:

modifica: chiarire il valore fuori intervallo nel messaggio di errore significherebbe interpretare il mese come 30 e il giorno come 11.


8

Non capisco perché i dati vengano convertiti da varchar a datetime quando 'Creato' è impostato su datetime

I valori letterali forniti per il confronto con la Createdcolonna sono stringhe. Per confrontare tali valori letterali con la datetimecolonna, SQL Server tenta di convertire le stringhe in datetimetipi, in base alle regole della precedenza del tipo di dati . Senza informazioni esplicite sul formato delle stringhe, SQL Server segue le sue regole contorte per l'interpretazione delle stringhe come periodi di dati.

A mio avviso, il modo migliore per evitare questo tipo di problemi è quello di essere espliciti sui tipi. SQL Server fornisce le CAST and CONVERTfunzioni a tale scopo. Quando si lavora con stringhe e tipi di data / ora, CONVERTè preferibile perché fornisce un parametro di stile per definire esplicitamente il formato della stringa.

La domanda utilizza stringhe in formato canonico ODBC (con millisecondi) (stile 121). Essere espliciti sul tipo di dati e sullo stile di stringa comporta quanto segue:

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

Detto questo, ci sono buone ragioni (come sottolinea Aaron nella sua risposta ) per usare una gamma semiaperta invece di BETWEEN(io uso lo stile 120 di seguito solo per varietà):

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

Essere espliciti sui tipi è un'abitudine molto buona da prendere in considerazione, in particolare quando si tratta di date e orari.



3

Un'altra alternativa, raccomando di usare letterali datetime ODBC . Nonostante il loro nome, non richiedono la connessione tramite ODBC. Evitano le solite regole di conversione in SQL Server e vengono sempre interpretate come 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'};


Gli altri datetimeletterali ODBC supportati sono De Tcome documentato qui nella documentazione online. Entrambi restituiscono datetime(non dateo time), ma la sintassi è ancora compatta e inequivocabile. I formati fissi per le stringhe sono:

Formati stringa ODBC

Esempio:

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 restituisce l'ora specificata nel giorno corrente , come riportato solo per uso interno {fn getdateODBC()}:

Progetto esecutivo


1
Beh, probabilmente lo farei solo CONVERT(DATE, '20141201')se il tuo bisogno di essere esplicito prevalesse su tutto il resto. Inoltre, se la colonna sottostante è un tipo di data / ora, questo non è davvero necessario. Dici WHERE Active = CONVERT(BIT, 1)di evitare WHERE Active = 1di essere interpretato come un INT?
Aaron Bertrand

3
@AaronBertrand In realtà, sono stato conosciuto per fare esattamente questo :) Ed ecco un esempio del perché .
Paul White 9

-1

il codice seguente, ottiene la sessione corrente dateformat, riceve un errore quando si converte in datetime, quindi imposta il formato della data su ymd e ultimo ma non meno importante testare nuovamente la conversione (casting) e funziona

-- 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 ottobre 2016 18:17:30 Copyright (c) Microsoft Corporation Enterprise Edition: licenze basate su core (64 bit) su Windows Server 2012 R2 Datacenter 6.3 (Build 9600:) (Hypervisor)


ti interessa spiegare il voto negativo ?, il codice funziona bene qui!
Marcello Miorelli,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.