Risposte:
Dato che stai usando il datetimetipo di dati, devi capire come il server sql arrotonda i dati del datetime.
╔═══════════╦═════╦═════════════════════════════╦═════════════════════════════╦══════════╦═══════════╗
║ Name ║ sn ║ Minimum value ║ Maximum value ║ Accuracy ║ Storage ║
╠═══════════╬═════╬═════════════════════════════╬═════════════════════════════╬══════════╬═══════════╣
║ datetime ║ dt ║ 1753-01-01 00:00:00.000 ║ 9999-12-31 23:59:59.997 ║ 3.33 ms ║ 8 bytes ║
║ datetime2 ║ dt2 ║ 0001-01-01 00:00:00.0000000 ║ 9999-12-31 23:59:59.9999999 ║ 100ns ║ 6-8 bytes ║
╚═══════════╩═════╩═════════════════════════════╩═════════════════════════════╩══════════╩═══════════╝
Utilizzando la query di seguito, è possibile vedere facilmente il problema dell'arrotondamento del server SQL quando si utilizza il DATETIMEtipo di dati.
select '2015-07-27 00:00:00.000' as Original_startDateTime,
convert(datetime ,'2015-07-27 00:00:00.000') as startDateTime,
'2015-07-27 23:59:59.999' as Original_endDateTime,
convert(datetime ,'2015-07-27 23:59:59.999') as endDateTime,
'2015-07-27 00:00:00.000' as Original_startDateTime2,
convert(datetime2 ,'2015-07-27 00:00:00.000') as startDateTime2, -- default precision is 7
'2015-07-27 23:59:59.999' as Original_endDateTime2,
convert(datetime2 ,'2015-07-27 23:59:59.999') as endDateTime2 -- default precision is 7
DATETIME2è in circolazione da SQL Server 2008, quindi inizia a usarlo invece di DATETIME. Per la tua situazione, puoi usare datetime2con precisione di 3 decimali, ad es datetime2(3).
Vantaggi dell'utilizzo datetime2:
datetimesupporto solo di 3 posizioni decimali .. e quindi si vede il problema degli arrotondamenti poiché per impostazione predefinita datetimearrotonda il più vicino .003 secondscon incrementi di .000, .003o .007secondi.datetime2è molto più preciso di datetimee datetime2ti dà il controllo DATEe TIMEal contrario datetime.Riferimento:
DateTime2vs DateTime.: a. Per - la - vasta - maggioranza - del - mondo reale - casi d'uso, i vantaggi di DateTime2molto <costi. Vedi: stackoverflow.com/questions/1334143/… b. Questo non è il problema alla radice qui. Vedi il prossimo commento
datetime3con 70 (contro 7) cifre di precisione aggiunte?). La migliore pratica consiste nell'utilizzare un valore in cui la precisione non ha importanza, ovvero <l' inizio del secondo, minuto, ora o giorno successivo vs. <= la fine del secondo, minuto, ora o giorno precedenti.
Come molti altri hanno menzionato nei commenti e altre risposte alla tua domanda, il problema principale 2015-07-27 23:59:59.999è stato arrotondato 2015-07-28 00:00:00.000da SQL Server. Secondo la documentazione per DATETIME:
Intervallo di tempo: da 00:00:00 a 23: 59: 59.997
Si noti che l'intervallo di tempo non può mai essere .999. Più in basso nella documentazione specifica le regole di arrotondamento utilizzate da SQL Server per la cifra meno significativa.
Si noti che la cifra meno significativa può avere solo uno dei tre valori potenziali: "0", "3" o "7".
Esistono diverse soluzioni / soluzioni alternative per questo che è possibile utilizzare.
-- Option 1
SELECT
*
FROM A
WHERE posted_date >= '2015-07-27 00:00:00.000'
AND posted_date < '2015-07-28 00:00:00.000' --Round up and remove equality
-- Option 2
SELECT
*
FROM A
WHERE posted_date >= '2015-07-27 00:00:00.000'
AND posted_date <= '2015-07-27 23:59:59.997' --Round down and keep equality
-- Option 3
SELECT
*
FROM A
WHERE CAST(posted_date AS DATE) = '2015-07-27' -- Use different data type
-- Option 4
SELECT
*
FROM A
WHERE CONVERT(CHAR(8), DateColumn, 112) = '20150727' -- Cast to string stripping off time
-- Option 5
SELECT
*
FROM A
WHERE posted_date BETWEEN '2015-07-27 00:00:00.000'
AND '2015-07-27 23:59:59.997' --Use between
Tra le cinque opzioni che ho presentato sopra, prenderei in considerazione le opzioni 1 e 3 le uniche opzioni praticabili. Trasmettono chiaramente le tue intenzioni e non si romperanno se aggiorni i tipi di dati. Se stai usando SQL Server 2008 o più recente, penso che l'opzione 3 dovrebbe essere il tuo approccio preferito. Ciò è particolarmente vero se è possibile passare dall'uso del DATETIMEtipo di dati a un DATEtipo di dati per la posted_datecolonna.
Per quanto riguarda l'opzione 3, una buona spiegazione su alcuni problemi può essere trovata qui: Cast fino ad oggi è sargable ma è una buona idea?
Non mi piacciono le opzioni 2 e 5 perché i .997secondi frazionari saranno solo un altro numero magico che le persone vorranno "aggiustare". Per alcuni altri motivi per cui BETWEENnon è ampiamente accettato, potresti voler dare un'occhiata a questo post .
Non mi piace l'opzione 4 perché convertire i tipi di dati in una stringa a scopo di confronto mi sembra sporco. Un motivo più qualitativo per evitarlo in SQL Server è che influisce sulla sargibilità, ovvero che non è possibile eseguire una ricerca dell'indice e ciò comporterà spesso prestazioni peggiori .
Per ulteriori informazioni sulla strada giusta e sul modo sbagliato di gestire le query sull'intervallo di date, consulta questo post di Aaron Bertrand .
Nel separarti sarai in grado di mantenere la tua query originale e si comporterebbe come desiderato se cambi la tua posted_datecolonna da a DATETIMEa a DATETIME2(3). Ciò consentirebbe di risparmiare spazio di archiviazione sul server, offrire una maggiore accuratezza con la stessa precisione, essere più conformi agli standard / portatili e consentire di regolare facilmente l'accuratezza / precisione se le esigenze cambieranno in futuro. Tuttavia, questa è solo un'opzione se si utilizza SQL Server 2008 o versioni successive.
Come un po 'di curiosità, la 1/300seconda precisione con DATETIMEsembra essere una sospensione da UNIX per questa risposta StackOverflow . Sybase che ha un'eredità condivisa ha una 1/300seconda precisione simile nei loro tipi di dati DATETIMEeTIME , ma le loro cifre meno significative sono leggermente diverse in "0", "3" e "6". A mio avviso, la 1/300precisione di un secondo e / o 3,33 ms è una decisione architettonica sfortunata poiché il blocco di 4 byte per il tempo nel DATETIMEtipo di dati di SQL Server avrebbe potuto facilmente supportare una precisione di 1 ms.
datetime3con 70 (vs. 7) cifre di precisione aggiunte? La migliore pratica consiste nell'utilizzare un valore in cui la precisione non ha importanza, ovvero <l'inizio del secondo, minuto, ora o giorno successivo rispetto a <= la fine del secondo, minuto, ora o giorno precedente.
Conversione implicita
Ho supposto che il tipo di dati di date_date sia Datetime. Tuttavia, non importa se il tipo sull'altro lato è Datetime, Datetime2 o solo Time perché la stringa (Varchar) verrà convertita implicitamente in Datetime.
Con posts_date dichiarata come Datetime2 (o Time), la posted_date <= '2015-07-27 23:59:59.99999'clausola where ha esito negativo poiché purugh 23:59:59.99999è un valore Datetime2 valido, questo non è un valore Datetime valido:
Conversion failed when converting date and/or time from character string.
Intervallo di tempo per Datetime
L'intervallo di tempo di Datetime è compreso tra 00:00:00 e 23: 59: 59.997. Pertanto 23: 59: 59.999 non è compreso nell'intervallo e deve essere arrotondato per eccesso o per difetto al valore più vicino.
Precisione
Inoltre, i valori Datetime sono arrotondati con incrementi di .000, .003 o .007 secondi. (ad es. 000, 003, 007, 010, 013, 017, 020, ..., 997)
Questo non è il caso del valore 2015-07-27 23:59:59.999compreso in questo intervallo: 2015-07-27 23:59:59.997e 2015-07-28 0:00:00.000.
Questo intervallo corrisponde alle opzioni precedenti e seguenti più vicine, entrambe che terminano con .000, .003 o .007.
Arrotondamento verso l'alto o verso il basso ?
Perché è più vicino al 2015-07-28 0:00:00.000(+1 contro -2) rispetto 2015-07-27 23:59:59.997, la stringa viene arrotondato per eccesso e diventa questo valore Datetime: 2015-07-28 0:00:00.000.
Con un limite superiore come 2015-07-27 23:59:59.998(o .995, .996, .997, .998), sarebbe stato arrotondato per difetto 2015-07-27 23:59:59.997e la query avrebbe funzionato come previsto. Tuttavia non sarebbe stata una soluzione ma solo un valore fortunato.
Tipi di Datetime2 o Time
Datetime2 e Time intervalli di tempo sono 00:00:00.0000000attraverso 23:59:59.9999999con una precisione di 100 ns (l'ultima cifra quando utilizzato con una precisione 7 cifre).
Tuttavia, un intervallo Datetime (3) non è simile all'intervallo Datetime:
0:0:00.000a23:59:59.9970:0:00.000000000a23:59:59.999Soluzione
Alla fine è più sicuro cercare le date al di sotto del giorno successivo rispetto alle date al di sotto o uguali a quello che pensi sia l'ultimo frammento di tempo del giorno. Questo principalmente perché sai che il giorno successivo inizia sempre alle 0: 00: 00.000 ma diversi tipi di dati potrebbero non avere lo stesso orario alla fine della giornata:
Datetime `0:0:00.000` to `23:59:59.997`
Datetime2 `0:0:00.000000000` to `23:59:59.999-999-900`
Time2 `0:0:00.000000000` to `23:59:59.999-999-900`
< 2015-07-28 0:00:00.000ti darà risultati accurati ed è l'opzione migliore<= 2015-07-27 23:59:59.xxx può restituire valori imprevisti quando non è arrotondato per eccesso a ciò che si pensa che dovrebbe essere.Potremmo pensare che cambiando [data_data] in Datetime2 e la sua maggiore precisione potrebbe risolvere questo problema ma non sarà d'aiuto perché la stringa è ancora convertita in Datetime. Tuttavia, se viene aggiunto un cast cast(2015-07-27 23:59:59.999' as datetime2), questo funziona bene
Trasmetti e converti
Il cast può convertire un valore con un massimo di 3 cifre in Datetime o con un massimo di 9 cifre in Datetime2 o Tempo e arrotondarlo alla precisione corretta.
Va notato che Cast di Datetime2 e Time2 può dare risultati diversi:
select cast('20150101 23:59:59.999999999' as datetime2(7)) è arrotondato per primo 2015-05-03 00: 00: 00.0000000 (per un valore superiore a 999999949)select cast('23:59:59.999999999' as time(7)) => 23: 59: 59.9999999Risolve il problema che il datetime sta riscontrando con gli incrementi di 0, 3 e 7, sebbene sia sempre meglio cercare le date prima del primo nano secondo del giorno successivo (sempre 0: 00: 00.000).
MSDN di origine: datetime (Transact-SQL)
Si sta arrotondando
select cast('2015-07-27 23:59:59.999' as datetime)
returns 2015-07-28 00:00:00.000
.998, .997, .996, .995 tutto cast / round a .997
Dovrebbe usare
select *
from A
where posted_date >= '2015-07-27 00:00:00.000'
and posted_date < '2015-07-28 00:00:00.000'
o
where cast(posted_date as date) = '2015-07-27'
Vedi la precisione in questo link
Sempre segnalato come .000, .003, .007
select * from A where date(posted_date) = '2015-07-27'
'DATE' is not a recognized built-in function name.
gives you control of DATE and TIME as opposed to datetime.cosa significa?