【问题标题】:How to query SQL Server table based on a specific date如何根据特定日期查询 SQL Server 表
【发布时间】:2008-12-26 19:47:33
【问题描述】:

我在 SQL Server 2005 中有一个包含三列的表:

id (int), 
message (text), 
timestamp (datetime)

时间戳和id都有索引。

我有兴趣进行查询以检索给定日期的所有消息,例如“2008 年 12 月 20 日”。但是我知道简单地执行 where timestamp='12/20/2008' 不会给我正确的结果,因为该字段是一个日期时间字段。

有人建议使用 DATEPART 函数并从时间戳中提取年、月和日,并验证它们分别等于 2008、12 和 20。看起来这不会使用我在时间戳上的索引,最终会进行全表扫描。

那么,构建查询以利用已创建的索引的最佳方法是什么?

【问题讨论】:

    标签: sql sql-server database


    【解决方案1】:

    根据我的经验,使用两个日期时间变量总是很可靠。该决议的问题似乎极不可能。然而,要记住的重要事实是(任何类型的)范围都包括两个端点。因此,您不能在两个日期上使用 BETWEEN 进行测试,因为它将同时包含这两个日期。而是使用类似

    日期字段 >= @startdate AND 日期字段

    The Manual.

    来吧,伙计们——这方面的文档并不难找到。 :D

    【讨论】:

      【解决方案2】:
      -- avoid re-calculating @MyDate +1 for every row
      DECLARE @NextDay DateTime
      Set @NextDay = @MyDate + 1
      
      SELECT 
          -- ...
      WHERE [timestamp] >= @MyDate AND [timestamp] < @NextDay
      

      【讨论】:

      • SQL 服务器会不会不够聪明,无法理解它只需要为语句计算一次 @MyDate + 1 吗?
      • 可能。不过,我知道它不会为 getdate() 做这件事。当然这是一个函数调用,这有点不同。但我偏执于将任何我不需要的东西留在 where 子句中。
      • 如果@MyDate 有时间部分,这将不起作用。要修复该使用: Where [timestamp] >= DateAdd(day, DateDiff(day, 0, @Date), 0) And [timestamp]
      • 大声笑,如果你能提供帮助,也不要在 where 子句中调用 DateAdd()。我发布的内容将为您提供 24 小时的范围。如果您还需要从@MyDate 截断时间,最快的方法是这样的: Set @MyDate = Cast(Floor(Cast(@MyDate As Int)) AS DateTime)
      • @Joel,不... DateAdd 是从 DateTime 值中去除时间的最快方法,只要您仅在 wheree 子句谓词的输入参数侧应用它,就绝对有对查询的 SARGability 没有影响
      【解决方案3】:

      BETWEEN 语句可以帮助您。

      SELECT *
      FROM MyTable
      WHERE TimeStamp BETWEEN @Start AND @End;
      

      在您想要接收消息的那一天,开始必须是上午 12:01,在同一天结束时,结束应该是晚上 11:59。

      【讨论】:

      • 12:00:00.000 vs 11:59:59.999 让我更容易使用两个比较和适当的运算符,就像我之前展示的那样。
      • @Joel - Between 与您的运营商做同样的事情,但更容易阅读(至少 IMO)。
      • 不,它没有。我创建了另一个答案来演示,因为这里没有空间。
      • SQL Server 文档说,如果 test_expression 的值大于或等于 begin_expression 的值且小于或等于 end_expression 的值,则 BETWEEN 返回 TRUE。
      • @Joel,Between(使用 Stephen 指定的构造,晚上 11:59:59:999),做的事情和你建议的完全一样……
      【解决方案4】:

      BETWEEN 不做 >=, <.>=,

      declare @low datetime
      declare @high datetime
      set @low = getdate()
      set @high = @low+1
      
      select case when @low between @low and @high then 1 else 0 end, 
          case when @high between @low and @high then 1 else 0 end
      

      结果将是 1,1,表明 = 应用于两个边界。

      【讨论】:

        【解决方案5】:

        假设@Date 是您想要所有消息的日期的任何日期时间的日期时间值,请使用此

        Where [timestamp] >= DateAdd(day, DateDiff(day, 0, @Date), 0) 
          And [timestamp] <  DateAdd(day, DateDiff(day, 0, @Date), 1) 
        

        这比使用 CAST 快得多,更不用说在日期时间上使用 CAST 时,如果将中午之后的日期时间值转换为整数,

        Declare @MyDate as Datetime
        Set @MyDate = '12/25/2008 12:01:00'
        Declare @IntVal Integer
        Set @IntVal = Cast(@MyDate as Integer) 
        Select Cast(@IntVal as DateTime)
        

        它将向上舍入到表示下一天日期的整数。上面的脚本将输出 12/26/2008

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-11-13
          • 1970-01-01
          • 2021-09-19
          • 1970-01-01
          • 2021-09-15
          • 2014-08-29
          相关资源
          最近更新 更多