2010-01-12 1 views
8

Я столкнулся с нечетной ошибкой, использующей поля datetime в SQL Server 2005. Поле datetime отображается с точностью до миллисекундного уровня, но похоже, что миллисекунды не являются всегда используется. Вот мой тестовый запрос:Запрос в полях datetime с миллисекундами дает неправильный результат в 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 

В мои результаты я получаю:

 
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 

Проблема заключается в том, что я получил 2010-01-01 дату, несмотря на то, что не должно быть меньше или равно к «2009-12-31 23: 59: 59.999». Но если я изменил запрос на использование «2009-12-31 23: 59: 59.99 « он работает нормально (время возврата не возвращается).

Это ошибка, или это как раз то, как работает SQL Server? Если это так, то есть ли для этого причина? Я столкнулся с тем, что переносил некоторые запросы из MySQL, где это работает как ожидалось (хотя MySQL даже не хранит миллисекунды!).

ответ

12

SQL Server хранит временную часть как номер 1/300 секундные длинные тики с полуночи.

23:59:59.999 округляется до ближайшего клеща, который является 00:00:00.000 следующего дня.

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 

В первом значении, дата часть, 0x9B8F (39823) число дней, прошедших с Jan 1st, 1900, а время часть, 0, это количество тиков с полуночи.

Во втором значении 0x018B81FF (25919999, или 24 * 60 * 60 * 300 - 1) - максимально возможное количество тиков с полуночи.

Наконец, третье значение имеет значение 0 во временной части, а часть даты - на единицу.

+2

Если это вариант в вашем коде, использование меньше: «... И col1 <'2010-1-1 00: 00: 00.0' –

+4

Просто добавьте ссылку: http://msdn.microsoft.com/en-us/library/ms187819.aspx 'Точность: округленная до инкрементов .000, .003 или .007 секунд' –

+0

3.33-миллисекундные тики. Интересно, почему они выбрали это? Если они хранят его как 32-битную дату и 32-битное время, общее количество миллисекунд в день составляет всего 0x05265c00, что соответствует 32-битовому целому числу с запасным номером. – Jenni

2

Это не ошибка. Это вполне ожидаемое поведение. посмотреть здесь: datetime and smalldatetime

Вы должны изменить его

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

со всеми типами с плавающей запятой, и дата/время, на самом деле своего рода значение с плавающей точкой, вы должны стараться избегать сравнений равенства, как, что ,

Таким образом, вместо:

WHERE x <= 10.999 

вы должны сделать:

WHERE x < 11 

так вместо перечисления до последнего значения вы хотите включить, вы перечисляете первое значение, которое вы хотите исключить, как правило, это работает лучше, когда границы являются целыми числами, так как они имеют гораздо больший шанс быть точно представимыми в области типа.

В вашем конкретном случае, я хотел бы изменить его:

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

Это также объясняет, почему «Между» Оператор сравнения не рекомендуется для дат. «Между» является всеобъемлющим сравнение (значения, которые соответствуют оба конца включены) и, как рекомендовано выше, вы должны делать эксклюзивную версию между такими, как bottomvalue < TestValue < topvalue

Смежные вопросы