Como está utilizando el datetimetipo de datos, debe comprender cómo el servidor sql redondea los datos de fecha y hora.
╔═══════════╦═════╦═════════════════════════════╦═════════════════════════════╦══════════╦═══════════╗
║ 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 ║
╚═══════════╩═════╩═════════════════════════════╩═════════════════════════════╩══════════╩═══════════╝
Usando la consulta a continuación, puede ver fácilmente el problema de redondeo que hace el servidor sql cuando usa DATETIME
el tipo de datos.
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
Click para agrandar
DATETIME2
ha existido desde SQL Server 2008, así que comience a usarlo en lugar de DATETIME
. Para su situación, puede utilizar datetime2
con precisión de 3 decimales, por ejemplo datetime2(3)
.
Beneficios de usar datetime2
:
- Admite hasta 7 lugares decimales para el componente de tiempo frente a
datetime
admitir solo 3 lugares decimales ... y, por lo tanto, ve el problema de redondeo ya que de forma predeterminada datetime
redondea el más cercano .003 seconds
con incrementos de .000
, .003
o .007
segundos.
datetime2
es mucho más preciso datetime
y datetime2
le da control DATE
y TIME
no datetime
.
Referencia:
gives you control of DATE and TIME as opposed to datetime.
¿Qué significa eso?DateTime2
vsDateTime
.: a. Para la gran mayoría de los casos de uso en el mundo real , beneficios deDateTime2
Mucho <costos. Ver: stackoverflow.com/questions/1334143/… b. Ese no es el problema raíz aquí. Ver siguiente comentario.datetime3
agregan 70 (vs. 7) dígitos de precisión?). La mejor práctica es usar un valor donde la precisión no importa, es decir, <el comienzo del siguiente segundo, minuto, hora o día vs. <= el final del segundo, minuto, hora o día anterior.Como varios otros han mencionado en los comentarios y otras respuestas a su pregunta es el tema central
2015-07-27 23:59:59.999
se redondea al2015-07-28 00:00:00.000
por SQL Server. Según la documentación para DATETIME:Tenga en cuenta que el rango de tiempo nunca puede ser
.999
. Más abajo en la documentación, especifica las reglas de redondeo que SQL Server usa para el dígito menos significativo.Observe que el dígito menos significativo solo puede tener uno de tres valores potenciales: "0", "3" o "7".
Hay varias soluciones / soluciones para esto que puede usar.
De las cinco opciones que he presentado anteriormente, consideraría las opciones 1 y 3 como las únicas opciones viables. Transmiten su intención claramente y no se romperán si actualiza los tipos de datos. Si está utilizando SQL Server 2008 o más reciente, creo que la opción 3 debería ser su enfoque preferido. Eso es especialmente cierto si puede cambiar el uso del DATETIMEtipo de datos a un DATEtipo de datos para su
posted_date
columna.Con respecto a la opción 3, se puede encontrar una muy buena explicación sobre algunos problemas aquí: el reparto hasta la fecha es modificable, pero ¿es una buena idea?
No me gustan las opciones 2 y 5 porque los
.997
segundos fraccionarios serán solo otro número mágico que la gente querrá "arreglar". Por algunas razones más por lasBETWEEN
que no se acepta ampliamente, es posible que desee ver esta publicación .No me gusta la opción 4 porque convertir los tipos de datos en una cadena para fines de comparación me parece sucio. Una razón más cualitativa para evitarlo en SQL Server es que afecta la capacidad de rastreo, es decir , que no puede realizar una búsqueda de índice y que con frecuencia dará como resultado un rendimiento más deficiente.
Para obtener más información sobre la forma correcta y la manera incorrecta de manejar las consultas de rango de fechas, consulte esta publicación de Aaron Bertrand .
Al partir, podrá mantener su consulta original y se comportará como lo desee si cambia su
posted_date
columna de DATETIMEa aDATETIME2(3)
. Eso ahorraría espacio de almacenamiento en el servidor, le daría una mayor precisión con la misma precisión, sería más compatible con los estándares / portátil y le permitiría ajustar fácilmente la precisión / precisión si sus necesidades cambian en el futuro. Sin embargo, esta es solo una opción si está utilizando SQL Server 2008 o posterior.Como un poco de trivia, la
1/300
segunda precisión DATETIMEparece ser una retención de UNIX según esta respuesta de StackOverflow . Sybase que tiene una herencia compartida tiene un similares1/300
de un segundo de precisión en susDATETIME
yTIME
tipos de datos pero sus dígitos menos significativos son un toque diferente a "0", "3" y "6". En mi opinión, la1/300
precisión de un segundo y / o 3,33 ms es una decisión arquitectónica desafortunada ya que el bloque de 4 bytes por el momento en el DATETIMEtipo de datos de SQL Server podría haber admitido fácilmente la precisión de 1 ms.fuente
datetime3
agregan 70 (vs. 7) dígitos de precisión? La mejor práctica es usar un valor donde la precisión no importa, es decir, <el comienzo del siguiente segundo, minuto, hora o día vs. <= el final del segundo, minuto, hora o día anterior.Supuse que el tipo de datos post_date es Datetime. Sin embargo, no importa si el tipo en el otro lado es Datetime, Datetime2 o simplemente Time porque la cadena (Varchar) se convertirá implícitamente en Datetime.
Con post_date declarado como Datetime2 (u Time), la
posted_date <= '2015-07-27 23:59:59.99999'
cláusula where falla porque aunque23:59:59.99999
es un valor válido de Datetime2, este no es un valor válido de Datetime:El rango de tiempo de Fecha y hora es de 00:00:00 a 23: 59: 59.997. Por lo tanto, 23: 59: 59.999 está fuera de rango y debe redondearse hacia arriba o hacia abajo al valor más cercano.
Además, los valores de fecha y hora se redondean en incrementos de .000, .003 o .007 segundos. (es decir, 000, 003, 007, 010, 013, 017, 020, ..., 997)
Este no es el caso con el valor
2015-07-27 23:59:59.999
que está dentro de este rango:2015-07-27 23:59:59.997
y2015-07-28 0:00:00.000
.Este rango corresponde a las opciones anteriores y siguientes más cercanas, ambas terminando con .000, .003 o .007.
Debido a que es más cercano a
2015-07-28 0:00:00.000
(1 frente a -2) que2015-07-27 23:59:59.997
, la cadena se redondea y se convierte este valor de fecha y hora:2015-07-28 0:00:00.000
.Con un límite superior como
2015-07-27 23:59:59.998
(o .995, .996, .997, .998), se habría redondeado hacia abajo2015-07-27 23:59:59.997
y su consulta habría funcionado como se esperaba. Sin embargo, no habría sido una solución sino un valor afortunado.Datetime2 y rangos de tiempo de tiempo son
00:00:00.0000000
a través de23:59:59.9999999
con una precisión de 100 ns (el último dígito, cuando se usa con una precisión de 7 dígitos).Sin embargo, un rango de fecha y hora (3) no es similar al rango de fecha y hora:
0:0:00.000
a23:59:59.997
0:0:00.000000000
a23:59:59.999
Al final, es más seguro buscar fechas por debajo del día siguiente que las fechas por debajo o iguales a lo que crees que es el último fragmento de la hora del día. Esto se debe principalmente a que sabe que el día siguiente siempre comienza a las 0: 00: 00,000 pero que los diferentes tipos de datos pueden no tener la misma hora al final del día:
< 2015-07-28 0:00:00.000
le dará resultados precisos y es la mejor opción<= 2015-07-27 23:59:59.xxx
puede devolver valores inesperados cuando no se redondea a lo que cree que debería ser.Podríamos pensar que cambiar [date_date] a Datetime2 y su mayor precisión podría solucionar este problema, pero no ayudará porque la cadena todavía se convierte a Datetime. Sin embargo, si se agrega un elenco
cast(2015-07-27 23:59:59.999' as datetime2)
, esto funciona bienCast puede convertir un valor de hasta 3 dígitos a Datetime o con hasta 9 dígitos a Datetime2 u Time y redondearlo a la precisión correcta.
Cabe señalar que Cast of Datetime2 y Time2 pueden dar resultados diferentes:
select cast('20150101 23:59:59.999999999' as datetime2(7))
es redondeado 2015-05-03 00: 00: 00.0000000 (para un valor superior a 999999949)select cast('23:59:59.999999999' as time(7))
=> 23: 59: 59.9999999En cierto modo, soluciona el problema que la fecha y hora tiene con los incrementos de 0, 3 y 7, aunque siempre es mejor buscar fechas antes del primer nano segundo del día siguiente (siempre 0: 00: 00.000).
Fuente MSDN: fecha y hora (Transact-SQL)
fuente
Esta redondeando
.998, .997, .996, .995 todos emitidos / redondos a .997
Debería usar
o
Vea la precisión en este enlace
Siempre informado como .000, .003, .007
fuente
fuente
'DATE' is not a recognized built-in function name.