【问题标题】:SQL Date Compare by only using Date not TimeSQL 日期比较仅使用日期而不是时间
【发布时间】:2013-02-07 18:19:34
【问题描述】:

这本来是一个关于如何实现它的问题,因为我被困在某个部分,但我现在很好奇为什么会发生这种情况。我只需要比较日期而不是时间,如果时间没有差异,这不会成为问题。下面的代码显示了我最初尝试的查询

SELECT *
FROM Employee e
inner join OT_Hours o on o.Emp_ID=e.Emp_ID
inner join Position p on p.Position_ID=e.Position_ID
inner join Signup_Sheet s on s.Employee_ID=e.Emp_ID
WHERE e.Eligible_OT=1 and s.Day_Shift = 1 
and p.Position_Name = 'Controller' 
and Convert(Varchar(20),s.Date,101) = '07/26/2010'
and Convert(Varchar(20),o.Date,101) <='07/26/2010'
and Convert(Varchar(20),o.Date,101) > '07/26/2009'
and o.Quantity NOT IN(0.3) order by o.Date DESC

我在运行该查询时不会得到任何结果,但是当我删除倒数第二行时,它将返回 12 个结果 ()。在查看数据后,我可以看到其中 4 个结果应该已经返回。现在是奇怪的部分。以下是我目前正在使用的代码。

SELECT DISTINCT o.Date, e.Emp_ID as Emp_ID, e.First_Name+ ' ' +e.Last_Name as Name, o.Quantity as Sum
FROM Employee e
left join OT_Hours o on o.Emp_ID=e.Emp_ID
left join Position p on p.Position_ID=e.Position_ID
left join Signup_Sheet s on s.Employee_ID=e.Emp_ID
WHERE e.Eligible_OT=1 and s.Day_Shift = 1 
and p.Position_Name = 'Controller' 
and Convert(Varchar(20),s.Date,101) = '07/26/2010'
and o.Date between '07/26/2009' and '07/26/2010'
and o.Quantity NOT IN(0.3) order by o.Date DESC

此查询将返回结果,但我也像在 o.Date 高于和低于指定日期时一样测试了它。当日期 8 个结果时。最终查询产生了 6 个结果。现在这不是我要查询的生产数据库,我是唯一使用它的人,所以数据没有改变。关于为什么会发生这种情况的任何解释?我假设它与将其转换为 varchar 有关,并且无法正确比较,但这并不能解释为什么我会得到 12 然后最终没有结果。另外,如果有人知道更好的实现方法,请告诉我。

【问题讨论】:

标签: sql sql-server tsql date-comparison


【解决方案1】:

这两个查询不一样 - 这个:

and o.Date between '07/26/2009' and '07/26/2010'

...相当于:

and o.Date >= '07/26/2009' 
and o.Date <= '07/26/2010'

BETWEEN 是 ANSI 标准,包含在我遇到的每个数据库中。

请注意,如果您没有为 DATETIME 指定时间部分,则该值默认为从当天的午夜开始 - 00:00:00。

【讨论】:

    【解决方案2】:

    我从 2007 年 2 月的 SQL Server 杂志(Itzik Ben-Gan 的日期时间计算)中学到了这项技术。这样,无论行的日期是否在午夜之后,您的“介于”都将起作用,因为通过此比较,所有内容都已标准化为午夜:

    select *
    from someTable
    where dateadd(day, datediff(day, 0, somedate), 0) between '07/26/2009' and '07/26/2010' 
    

    datediff 和 dateadd 一起工作以剥离时间并留下日期。然后,您可以将其与字符串文字或已完成相同修改的其他日期进行比较。我建议把它放在一个函数中。

    编辑: 基于 OMG Ponies 的 cmets。这不会利用日期列上的索引。除了其他人提到的技术之外,另一种方法可能是使用时间剥离技术。因此,不要在表格的列上执行此操作,而是在“之间”的最后一个参数上执行此操作。你可以有这样的功能:

    CREATE FUNCTION [dbo].[fn_enddate](@enddate datetime)
    RETURNS datetime AS  
    BEGIN
        DECLARE @endOfDay datetime
        set @endOfDay = dateadd(millisecond, -2, dateadd(day, datediff(day, 0, @enddate) + 1, 0))
        return @endOfDay
    END
    

    这需要参数日期,将其设置为第二天的午夜,然后减去两毫秒,给出给定日期时间的结束日期。那么你可以这样做:

    select *
    from someTable
    where somedate between '07/26/2009' and dbo.fn_enddate('07/26/2010')
    

    【讨论】:

    • 我没见过这个。很高兴知道。他们是否也谈到了'CAST(FLOOR(CAST(' 方法?
    • 我不记得了。他比较了几个,这个似乎是最好的。
    • 我不建议使用这个 - 在列上使用函数会呈现索引,如果在适当的位置,则无用。
    • @OMG Ponies - 有趣 - 绝对值得考虑。所以如果离开“内联”,索引会被使用吗?
    • @Ryan:抱歉,我不懂“左内联”?
    【解决方案3】:

    在第一个查询的倒数第三行中,您正在比较两个字符串。

    因此01/02/2009 大于01/01/2010

    我通常会约会BETWEEN '01/02/2009 00:00:00.000' AND '01/01/2010 23:59:59.997',但看到更好的解决方案会很有趣。

    【讨论】:

    • SQL Server 执行隐式数据类型转换,从字符串到日期时间提供格式匹配。
    • SELECT CASE WHEN '01/02/2009' &gt; '01/01/2010' THEN 1 ELSE 0 END 在我的 SQL 框中返回 1。 o.Date &gt; '07/26/2009' 将被隐式转换,但不是根据第一个查询的两个 varchars。
    • 不是替代方案,但您回答了我的问题,即为什么它没有正确比较日期。没有意识到 1/02/2009 大于 01/01/2010 哈哈
    • 使用 datediff 仅比较日期部分:DATEDIFF(day, FieldDate, GETDATE()) = 0 src: link
    【解决方案4】:
    ...
    AND s.Date BETWEEN '2010-07-26 00:00:00.000' AND '2010-07-26 23:59:59.997'
    AND o.Date BETWEEN '2009-07-26 00:00:00.000' AND '2010-07-26 23:59:59.997'
    ...
    

    【讨论】:

      【解决方案5】:

      我的建议是调整您的标准以涵盖时间,而不是日期本身。如果您转换或以其他方式操作列进行比较,您可能会破坏查询中索引的使用。

      此外,请始终将日期与日期进行比较。正如 Chris Diver 指出的那样,当您将它们转换为字符串并尝试进行比较时,您会遇到问题。

      在你的情况下,我会尝试:

      SELECT
          o.Date,
          e.Emp_ID as Emp_ID,
          e.First_Name+ ' ' +e.Last_Name as Name,
          o.Quantity as Sum
      FROM
          Employee e
      LEFT JOIN OT_Hours o ON o.Emp_ID = e.Emp_ID
      LEFT JOIN Position p ON p.Position_ID = e.Position_ID
      LEFT JOIN Signup_Sheet s ON s.Employee_ID = e.Emp_ID
      WHERE
          e.Eligible_OT = 1 AND
          s.Day_Shift = 1 AND
          p.Position_Name = 'Controller' AND
          (s.Date >= @signup_date AND s.Date < DATEADD(dy, 1, @signup_date)) AND
          (o.Date >= @order_start_date AND o.Date < DATEADD(dy, 1, @order_end_date)) AND
          o.Quantity NOT IN (0.3)
      ORDER BY
          o.Date DESC
      

      您需要确保参数或变量始终没有时间部分。

      【讨论】:

        【解决方案6】:

        如果您的数据库是 SQL Server,我所做的工作非常好,以剥离时间类似于以下内容......

        SELECT DISTINCT o.Date, e.Emp_ID as Emp_ID, e.First_Name+ ' ' +e.Last_Name as Name, o.Quantity as Sum 
        FROM Employee e 
        left join OT_Hours o on o.Emp_ID=e.Emp_ID 
        left join Position p on p.Position_ID=e.Position_ID 
        left join Signup_Sheet s on s.Employee_ID=e.Emp_ID 
        WHERE e.Eligible_OT=1 and s.Day_Shift = 1  
        and p.Position_Name = 'Controller'  
        and CAST(FLOOR(CAST(s.Date AS FLOAT)) AS DATETIME) = '07/26/2010' 
        and CAST(FLOOR(CAST(o.Date AS FLOAT)) AS DATETIME) between '07/26/2009' and '07/26/2010' 
        and o.Quantity NOT IN(0.3) order by o.Date DESC 
        

        根据参数的设置方式“07/26/2010”、“07/26/2009”,您可以将它们存储在日期时间变量中并执行相同的cast(floor(cast(@datevar as float)) as datetime) 操作。

        这似乎是一个转贴。在这里检查接受的答案...How to remove the time portion of a datetime value (SQL Server)?

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2021-01-24
          • 2023-04-01
          • 2016-09-19
          • 1970-01-01
          • 2014-09-11
          • 1970-01-01
          相关资源
          最近更新 更多