【问题标题】:Best way to check for current date in where clause of sql query在 sql 查询的 where 子句中检查当前日期的最佳方法
【发布时间】:2008-11-28 14:25:24
【问题描述】:

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

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))

【问题讨论】:

  • 请注意,您选择的答案不是最佳性能方式。你的方式接近正确的方式,你只需要切换到 >= 和

标签: sql tsql stored-procedures performance


【解决方案1】:
WHERE
  DateDiff(d, Received, GETDATE()) = 0

编辑:正如 cmets 对此答案所述,这不是一个理想的解决方案。检查此线程中的其他答案。

【讨论】:

  • 我不会那样做,因为它不支持 SARG。
  • @Mitch Wheat:只要没有仅包含日期部分的列并且上面有索引,那么无论如何您都无能为力。
  • Tomalak,不 - 他的初始解决方案是 SARGable... 接收位置 >= {Midnight This morning} 并且收到
  • 什么是“SARG-able”?
  • 表示可以使用索引(en.wikipedia.org/wiki/Sargable)来满足搜索条件。计算要过滤的值会使操作成为非 SARGable,因为计算的值不在索引中。这会强制服务器单独查看每一行(=慢)。
【解决方案2】:

如果您只想找到今天收到日期的所有记录,并且有未来收到日期的记录,那么您所做的是(非常非常轻微)错误......因为介于运算符允许值等于结束边界,因此您可以获得接收日期 = 到明天午夜的记录...

如果不需要在 Received 上使用索引,那么您需要做的就是检查与当前日期时间的日期差异是否为 0...

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

这个谓词当然不是 SARGable 所以它不能使用索引... 如果这是此查询的问题,那么假设您将来不能有 Received 日期,我会改用它...

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

如果收到的日期可以在将来,那么您可能已尽可能接近最有效的日期...(除了将 Between 更改为 >= AND

【讨论】:

  • Charles,即使没有索引,DateDiff(day, received, getdate()) 也不是最好的,因为它会强制对表中的每一行进行计算,无缘无故地使用更多 CPU。
  • @Emtucifor,是的,但与磁盘 I/O 读取相比,cpu 周期微不足道,以至于无关紧要。我们在这里谈论的是三到四个数量级的差异。
  • 没错,查尔斯。感谢您正确看待我的挑剔。 :) 我认为最好尽可能推荐后者,因为当有索引时,会严重影响 I/O。
  • @Emtucifor,你又是对的。当然,最重要的是,当你可以自行决定这样做时,就是放置适当的索引,并设计查询以使用它们.
【解决方案3】:

如果你想要性能,你想要直接命中索引,每行没有任何 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)如果它对于每个生成的行都是相同的值,查询处理器无论如何都会预先计算它,所以它只会计算一次,不不管查询产生多少行。
【解决方案4】:

这几乎是最好的方法。 您可以将 DATEADD(d, DATEDIFF(d, 0, GETDATE()), 0) 和 DATEADD(d, DATEDIFF(d, 0, GETDATE()), 1) 放入变量中并改用这些变量,但我没有认为这会提高性能。

【讨论】:

    【解决方案5】:

    我不确定您如何定义“最佳”,但这会很好。

    然而,如果这个查询是你要重复运行的东西,你应该去掉 get_date() 函数,并通过你运行它的任何编程语言在其中粘贴一个文字日期值。尽管他们的输出每 24 小时仅更改一次,get_date()、current_date() 等是 非确定性 函数,这意味着您的 RDMS 可能会使查询作为存储在其查询缓存中的候选对象无效,如果它有一个。

    【讨论】:

      【解决方案6】:

      怎么样

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

      【讨论】:

      • 这不是最好的方法。请参阅 Marc Gravell 的帖子了解最佳方法。
      【解决方案7】:

      我通常会使用solution suggested by 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 建议的解决方案远非最佳方法。
      【解决方案8】:

      转换成相同格式后比较两个日期,如下所示。

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-11-19
        • 1970-01-01
        • 1970-01-01
        • 2014-01-19
        • 1970-01-01
        • 2012-03-05
        • 1970-01-01
        相关资源
        最近更新 更多