Запрос к полям даты и времени с миллисекундами дает неверный результат в SQL Server - PullRequest
8 голосов
/ 12 января 2010

Я сталкиваюсь с странной ошибкой, используя поля даты и времени в SQL Server 2005. Поле даты и времени отображается с точностью до миллисекунды, но похоже, что миллисекунды используются не всегда. Вот мой тестовый запрос:

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 8 », он будет работать нормально (даты и время 2010 не возвращаются).

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

Ответы [ 4 ]

12 голосов
/ 12 января 2010

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 голосов
/ 12 января 2010

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

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

WHERE col1 >= '2009-12-31 00:00:00.0' AND col1 < '2010-01-01' 
1 голос
/ 30 октября 2015

По этой же причине оператор сравнения «Между» не рекомендуется для дат. «Между» - это инклюзивное сравнение (включены значения, которые соответствуют обоим концам), и, как рекомендовано выше, вы должны делать эксклюзивную версию между такими, как нижнее значение <тестовое значение <верхнее значение </p>

1 голос
/ 12 января 2010

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

Так что вместо:

WHERE x <= 10.999

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

WHERE x <  11

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

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

WHERE ... col1 < '2010-01-01 00:00:00.000'
               ^
               |              ^-- changed to 2010
               ^-- changed <= to <
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...