使用毫秒查询datetime字段会在SQL Server中产生错误的结果

时间:2010-01-12 14:32:49

标签: sql sql-server-2005 datetime

我在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 个答案:

答案 0 :(得分: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

在第一个值中,日期部分0x9B8F39823)是自Jan 1st, 1900以来的天数,时间部分0是数字从午夜开始的蜱虫。

在第二个值中,0x018B81FF2591999924 * 60 * 60 * 300 - 1)是自午夜以来最大可能的刻度数。

最后,第三个值在时间部分中有0,日期部分增加了一个。

答案 1 :(得分:2)

这不是错误。这是完全预期的行为。 看这里:datetime and smalldatetime

您应该将其更改为

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

答案 2 :(得分:1)

对于所有浮点类型,日期/时间实际上是一种浮点值,您应该尽量避免相等的比较。

所以而不是:

WHERE x <= 10.999

你应该这样做:

WHERE x <  11

因此,不是列出您想要包含的最后一个值,而是列出您想要排除的第一个值,通常这在边界是整数时更有效,因为它们有更大的机会在域中准确表示类型。

在您的特定情况下,我会将其更改为:

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

答案 3 :(得分:1)

这也是为什么不建议在日期之间使用'Between'比较运算符。 'Between'是一个包容性的比较(包含两端匹配的值),并且如上所述,您应该在两者之间做一个独有的版本,例如                   bottomvalue&lt;测试值&lt; topvalue