2010-01-12 12 views
8

Me encuentro con un error extraño usando campos de fecha y hora en SQL Server 2005. El campo de fecha y hora aparece con precisión de milisegundos, pero parece que los milisegundos no son siempre usado. Aquí está mi consulta de prueba:Consultar campos de fecha y hora con milisegundos da un resultado erróneo en SQL Server

SELECT col1, YEAR(col1) AS yr, MONTH(col1) AS mn, DAY(col1) AS dy 
FROM mytable 
WHERE col1 >= '2009-12-31 00:00:00.0' AND col1 <= '2009-12-31 23:59:59.999' 
ORDER BY col1 

En mis resultados me sale:

 
col1      | yr | mn | dy 
----------------------------+------+----+---- 
2009-12-31 00:00:00:00.000 | 2009 | 12 | 31 
2010-01-01 00:00:00:00.000 | 2010 | 1 | 1 

El problema es que tengo la fecha 2010-01-01, a pesar de que no debe ser menor o igual a "2009-12-31 23: 59: 59.999". Pero si cambio la consulta para usar "2009-12-31 23: 59: 59.99 " funciona bien (no se devuelven las fechas de 2010).

¿Es esto un error, o es así como funciona SQL Server? Si así es como funciona, ¿hay alguna razón para ello? Me encontré con esto migrando algunas consultas de MySQL, donde funciona como se esperaba (¡aunque MySQL ni siquiera almacena los milisegundos!).

Respuesta

12

SQL Server almacena la parte del tiempo como el número de 1/300 segundos ticks largos desde la medianoche.

23:59:59.999 se redondea al tic más cercano que pasa a ser 00:00:00.000 del día siguiente.

SELECT CAST(CAST('2009-12-01 00:00:00.000' AS DATETIME) AS BINARY(8)), 
     CAST(CAST('2009-12-01 23:59:59.997' AS DATETIME) AS BINARY(8)), 
     CAST(CAST('2009-12-01 23:59:59.999' AS DATETIME) AS BINARY(8)) 



0x00009B8F 00000000 0x00009B8F 018B81FF 0x00009B90 00000000 

En el primer valor, la parte de fecha, 0x9B8F (39823) es el número de días desde Jan 1st, 1900, y la parte de tiempo, 0, es el número de garrapatas desde la medianoche.

En el segundo valor, 0x018B81FF (25919999 o 24 * 60 * 60 * 300 - 1) es la cantidad máxima posible de tics desde la medianoche.

Finalmente, el tercer valor tiene el 0 en la parte de tiempo y la parte de fecha aumentó en uno.

+2

Si se trata de una opción en su código, el uso menor que: "... AND col1 <'2010-1-1 00: 00: 00.0' –

+4

Solo para agregar la referencia: http://msdn.microsoft.com/en-us/library/ms187819.aspx 'Exactitud: redondeado a incrementos de .000, .003 o .007 segundos' –

+0

tics de 3.33 milisegundos. Me pregunto por qué en la tierra eligieron eso? Si lo están almacenando como una fecha de 32 bits y un tiempo de 32 bits, el número total de milisegundos en un día es solo 0x05265c00, que se ajusta en un entero de 32 bits con espacio libre. – Jenni

2

Esto no es un error. Es un comportamiento perfectamente esperado. vistazo aquí: datetime and smalldatetime

se debe cambiar a

WHERE col1 >= '2009-12-31 00:00:00.0' AND col1 < '2010-01-01' 
1

Con todos los tipos de punto flotante, y una fecha/hora es en realidad una especie de valor de punto flotante, usted debe tratar de evitar las comparaciones de igualdad por el estilo .

Así que en lugar de:

WHERE x <= 10.999 

que debe hacer:

WHERE x < 11 

lo que en lugar de enumerar hasta el último valor que desee incluir, se indicará el primer valor que desea excluir, por lo general funciona este mejor cuando los límites son números enteros, ya que tienen una posibilidad mucho mayor de ser representados con precisión en el dominio del tipo.

En su caso particular, me gustaría cambiarlo a:

WHERE ... col1 < '2010-01-01 00:00:00.000' 
      ^
       |    ^-- changed to 2010 
       ^-- changed <= to < 
1

Esta es también la razón por la 'Entre' operador de comparación no se recomienda para las fechas. 'Entre' es una comparación incluido (valores que coinciden con los dos extremos se incluyen) y como se recomienda arriba, usted debe hacer una versión exclusiva de entre como bottomvalue < TestValue < topvalue

Cuestiones relacionadas