SQL查询中检查当前日期的最佳方法

6

我正在寻找最有效(性能最佳)的方法来检查日期字段是否为当前日期。目前我们使用的是:

SELECT     COUNT(Job) AS Jobs
FROM         dbo.Job
WHERE     (Received BETWEEN DATEADD(d, DATEDIFF(d, 0, GETDATE()), 0)
                        AND DATEADD(d, DATEDIFF(d, 0, GETDATE()), 1))

3
请注意,您选择的答案并不是最佳性能方式。您的方法接近正确的方法,只需要切换到使用">="和"<"而不是使用BETWEEN(BETWEEN在第二个条件上类似于"<=",是不正确的)。 - ErikE
8个回答

13
WHERE
  DateDiff(d, Received, GETDATE()) = 0

编辑:正如在本答案的评论中指出的那样,这不是一个理想的解决方案。也请查看本主题中的其他答案。


2
我不会那样做,因为它不是可SARG的。 - Mitch Wheat
1
Tomalak, 不,他的初始解决方案是SARGable... Where Received >= {Midnight This morning} And Received < {Midnight Tonight} 是SARGable。 - Charles Bretana
5
"SARG-able" 是什么意思? - James Curran
2
这意味着可以通过使用索引(http://en.wikipedia.org/wiki/Sargable)满足搜索条件。计算被过滤的值会使操作变得非SARGable,因为计算出的值不在任何索引中。这迫使服务器逐个查看每一行(=慢)。 - Tomalak
他正在寻求最佳方法,而这种方法不使用索引。不要使用它。结束。 - yakya
显示剩余7条评论

9

如果您只想查找接收日期为今天的所有记录,并且有未来接收日期的记录,则您所做的方法(非常非常轻微)是错误的。。。因为Between操作符允许等于结束边界的值,所以您可能会得到接收日期=到明天午夜的记录...

如果不需要在接收方面使用索引,则您只需要检查与当前日期时间的日期差是否为0即可...

Where DateDiff(day, received, getdate()) = 0

这个谓词显然不是SARGable,所以它不能使用索引...如果这是查询的问题,那么假设您不能有未来的接收日期,我会改用以下方法代替...

Where Received >= DateAdd(day, DateDiff(Day, 0, getDate()), 0) 

如果接收日期可以在未来,则您可能已经尽可能地高效...(除了将Between更改为>= 和 <)


即使没有使用索引,Charles也不建议使用DateDiff(day, received, getdate()),因为它会强制对表中的每一行进行计算,造成CPU资源浪费。 - ErikE
@Emtucifor,没错,但与磁盘I/O读取相比,CPU周期微不足道,可以忽略不计。这里的差异在三到四个数量级上。 - Charles Bretana
没错,Charles。感谢你让我认识到自己的吹毛求疵。 :) 我认为在可能的情况下最好推荐后者,因为当存在索引时,它会严重影响I/O。 - ErikE
@Emtucifor,你又一次说得对。当然,在有自主权的情况下,最好的方法是放置适当的索引,并设计查询以使用它们。 - Charles Bretana

5

如果你想要更好的性能,你需要直接命中索引,而不需要每行数据都进行CPU等操作。因此,我建议先计算范围,然后使用简单的WHERE查询。我不知道你正在使用哪个数据库,但在SQL Server中,以下代码可以实现此功能:

// ... where @When is the date-and-time we have (perhaps from GETDATE())
DECLARE @DayStart datetime, @DayEnd datetime
SET @DayStart = CAST(FLOOR(CAST(@When as float)) as datetime) -- get day only
SET @DayEnd = DATEADD(d, 1, @DayStart)

SELECT     COUNT(Job) AS Jobs
FROM         dbo.Job
WHERE     (Received >= @DayStart AND Received < @DayEnd)

我不确定你所说的“直接”命中索引是什么意思?如果你只是在谈论当谓词运算符的“另一侧”有一个计算时,而不是在执行索引之前预先计算它,那么要么A)计算值基于表中的其他列,并且对于生成的行不相同,因此必须在SQL中,要么B)如果它对于每个生成的行都是相同的值,则查询处理器将预先计算它,因此无论查询产生多少行,它都只会被计算一次。 - Charles Bretana

0

这基本上是最好的方法。 你可以将DATEADD(d, DATEDIFF(d, 0, GETDATE()), 0)和DATEADD(d, DATEDIFF(d, 0, GETDATE()), 1)放入变量中,然后使用它们,但我认为这不会提高性能。


0

我不确定你如何定义“最好”,但那个方法可以正常工作。

然而,如果这个查询是你要重复运行的内容,你应该摒弃get_date()函数,并直接用编程语言中的一个字面日期值替代。尽管它们的输出每24小时只会更改一次,get_date()、current_date()等等都是非确定性函数,这意味着如果你的关系数据库管理系统(RDMS)支持查询缓存,它可能会将这个查询无效化。


0

怎么样?

 WHERE
      DATEDIFF(d, Received, GETDATE()) = 0

这不是最好的方法。请查看Marc Gravell的帖子以获取最佳方法。 - ErikE

0

通常我会使用Tomalak建议的解决方案,但如果你真的需要更好的性能,最好的选择可能是添加一个额外的索引字段ReceivedDataPartOnly - 该字段将存储没有时间部分的数据,然后使用查询。

declare @today as datetime
set @today = datediff(d, 0, getdate())

select     
    count(job) as jobs
from         
    dbo.job
where     
    received_DatePartOnly = @today

Tomalak提出的解决方案远非最佳选择。 - ErikE

0

将两个日期转换为相同的格式后进行比较,例如以下格式。

where CONVERT(varchar, createddate, 1) = CONVERT(varchar, getdate(), 1);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接