8

我正在使用 SQL Server 2000 中的 T-SQL,并且我有一个表TRANSACTIONS,其中包含一个定义为 DateTime 的日期列TRANDATE,以及与此问题无关的许多其他列。

该表填充了跨越多年的事务。我遇到了代码,测试,这让我很困惑。有一个简单的SELECT,像这样:

SELECT TRANDATE, RECEIPTNUMBER FROM TRANSACTIONS WHERE TRANDATE BETWEEN '12/01/2010' and '12/31/2010' ORDER BY TRANDATE

并且它没有返回我知道该表中的两行数据。

使用上面的语句,它返回的最后一行按顺序具有TRANDATE:2010-12-31 00:00:00.000

当我像下面这样修改语句时,我得到了该表中 2010 年 12 月的另外两行:

SELECT TRANDATE, RECEIPTNUMBER FROM TRANSACTIONS WHERE TRANDATE BETWEEN '12/01/2010 00:00:00' and '12/31/2010 23:59:59' ORDER BY TRANDATE

我试图找出为什么BETWEEN操作员在使用上面的第一个时不包括 2010 年 12 月 31 日 24 期间的所有行SELECT。为什么它需要SELECT像在第二个已修改的语句中那样将明确的小时数添加到语句中,以使其提取正确数量的行?

是不是因为方式TRANDATE被定义为“ DATETIME”?

基于这一发现,我认为将不得不遍历所有这些旧代码,因为这些BETWEEN操作符在这个旧系统中到处都是,而且似乎它没有正确提取所有数据。我只是想先从一些人那里得到澄清。谢谢!

4

5 回答 5

14

日期是时间点,而不是时间跨度。

'12/31/2010'也是一个重点。也就是说,现在是 12 月 31 日的午夜。
此后发生的一切都将被忽略。
这正是您想要的行为(即使您还没有意识到这一点)。

不要认为当你选择省略时间部分时,它被神奇地假设为"any"。这将是"all zeroes",也就是午夜。

如果您想在查询中包含一整天而不需要指定23:59:59(顺便说一下,不包括当天的最后一秒,在当天时刻 23:59:59和第二天的时刻 00:00:00之间),您可以这样做通过使用严格的不等式 ( >, ) 以您想要<的第一个时间点为界:

WHERE TRANDATE >='12/01/2010 00:00:00' and TRANDATE < '01/01/2011'

或通过比较转换为的日期值DATE

WHERE CAST(TRANDATE AS DATE) between '12/01/2010' and '12/31/2010'

(可以将这种类型的转换放在一个WHERE子句中,它是 sargable)。

于 2011-03-25T14:53:06.280 回答
4

正如您所发现的,如果您在输入日期时未指定时间,则默认为该日期早上的午夜。因此,2010 年 12 月 31 日在当天开始时的午夜停止。

要获取 2010 年 12 月 31 日的所有日期,您可以指定时间,就像您所做的那样,或者在结束日期上增加一天。没有时间,2011 年 1 月 1 日在 2010 年 12 月 31 日午夜钟声结束。所以,你可以这样做BETWEEN 12/1/2010 AND 1/1/2011DATEADD如果这样更容易,您可以使用在 SQL 中添加日期。

第二种增加一天的方法存在一些风险。您将获得时间为 00:00:00 的 2011 年 1 月 1 日的任何记录。

这是执行以下操作的一种方法DATEADD

DECLARE @FromDate datetime, @ToDate datetime
// These might be stored procedure input parameters
SET @FromDate = '12/1/2010'
SET @ToDate = '12/31/2010'

SET @ToDate = DATEADD(d, 1, @ToDate)

然后你以通常的方式在短语中使用@ToDate你的WHERE从句。BETWEEN

于 2011-03-25T14:55:16.213 回答
3

“12/01/2010”表示“12/01/2010 00:00:00”,“12/31/2010”表示“12/31/2010 00:00:00”。这就是为什么在 2010 年 12 月 31 日当天晚些时候的日期时间值被排除在查询结果之外的原因。

于 2011-03-25T14:53:19.593 回答
2

如果我这样做,你的预期结果是什么

Insert "12/31/2010" into your datetime column?

确切地说:2010 年 12 月 31 日 00:00:00

那么,为什么您会期望它作为查询的参数而有所不同呢?

于 2011-03-25T14:52:44.890 回答
1

你已经回答了你自己的问题。您观察到的是SQL Server 的工作方式。

如果您需要确认,此MSDN 文档有以下说明

如果未指定时间部分,则默认为 12:00 AM 请注意,包含 1998-0105 的 12:00 AM 之后的时间部分的行不会被此查询返回,因为它超出了范围。

编辑

至于您的评论,日期时间本质上是一个浮点值。

以下脚本显示了 SQL Server 使用的数字。
当您的上限为 40541 (12/31/2010) 时,不能包含 40541.9749 (12/31/2010 23:23:59)

DECLARE @ADateTime1 DATETIME
DECLARE @ADateTime2 DATETIME
DECLARE @ADateTime1AsFloat FLOAT
DECLARE @ADateTime2AsFloat FLOAT

SET @ADateTime1 = '12/31/2010'
SET @ADateTime2 = '12/31/2010 23:23:59'

SET @ADateTime1AsFloat = CAST(@ADateTime1 AS FLOAT)
SET @ADateTime2AsFloat = CAST(@ADateTime2 AS FLOAT)

SELECT @ADateTime1AsFloat, @ADateTime2AsFloat
于 2011-03-25T14:52:50.513 回答