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 DATETIMEel 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
DATETIME2ha existido desde SQL Server 2008, así que comience a usarlo en lugar de DATETIME. Para su situación, puede utilizar datetime2con 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
datetimeadmitir solo 3 lugares decimales ... y, por lo tanto, ve el problema de redondeo ya que de forma predeterminada datetimeredondea el más cercano .003 secondscon incrementos de .000, .003o .007segundos.
datetime2es mucho más preciso datetimey datetime2le da control DATEy TIMEno datetime.
Referencia:
gives you control of DATE and TIME as opposed to datetime.¿Qué significa eso?DateTime2vsDateTime.: a. Para la gran mayoría de los casos de uso en el mundo real , beneficios deDateTime2Mucho <costos. Ver: stackoverflow.com/questions/1334143/… b. Ese no es el problema raíz aquí. Ver siguiente comentario.datetime3agregan 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.999se redondea al2015-07-28 00:00:00.000por 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_datecolumna.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
.997segundos fraccionarios serán solo otro número mágico que la gente querrá "arreglar". Por algunas razones más por lasBETWEENque 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_datecolumna 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/300segunda precisión DATETIMEparece ser una retención de UNIX según esta respuesta de StackOverflow . Sybase que tiene una herencia compartida tiene un similares1/300de un segundo de precisión en susDATETIMEyTIMEtipos de datos pero sus dígitos menos significativos son un toque diferente a "0", "3" y "6". En mi opinión, la1/300precisió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
datetime3agregan 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.99999es 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.999que está dentro de este rango:2015-07-27 23:59:59.997y2015-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.997y 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.0000000a través de23:59:59.9999999con 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.000a23:59:59.9970:0:00.000000000a23:59:59.999Al 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.000le dará resultados precisos y es la mejor opción<= 2015-07-27 23:59:59.xxxpuede 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.