如何比较SQL Server中的时间?

时间:2009-04-30 16:43:12

标签: sql sql-server datetime

我正在尝试在SQL查询中比较日期时间字段中的时间,但我不知道它是否正确。我不想比较日期部分,只是时间部分。

我这样做:

SELECT timeEvent 
FROM tbEvents 
WHERE convert(datetime, startHour, 8) >= convert(datetime, @startHour, 8)

这是对的吗?

我问这个是因为我需要知道08:00:00是否小于或大于07:30:00并且我不想比较日期,只需时间一部分。

谢谢!

17 个答案:

答案 0 :(得分:74)

您的比较将起作用,但它会很慢,因为日期会转换为每行的字符串。要有效地比较两个时间部分,请尝试:

declare @first datetime
set @first = '2009-04-30 19:47:16.123'
declare @second datetime
set @second = '2009-04-10 19:47:16.123'

select (cast(@first as float) - floor(cast(@first as float))) -
       (cast(@second as float) - floor(cast(@second as float)))
       as Difference

详细说明:SQL Server中的日期存储为浮点数。小数点前的数字代表日期。小数点后面的数字代表时间。

所以这是一个例子日期:

declare @mydate datetime
set @mydate = '2009-04-30 19:47:16.123'

让我们将其转换为浮动:

declare @myfloat float
set @myfloat = cast(@mydate as float)
select @myfloat
-- Shows 39931,8244921682

现在取数字之后的部分,即时间:

set @myfloat = @myfloat - floor(@myfloat) 
select @myfloat
-- Shows 0,824492168212601

将其转换回日期时间:

declare @mytime datetime
set @mytime = convert(datetime,@myfloat)
select @mytime
-- Shows 1900-01-01 19:47:16.123

1900-01-01只是“零”日期;你可以用转换显示时间部分,例如指定格式108,这就是时间:

select convert(varchar(32),@mytime,108)
-- Shows 19:47:16

datetime和float之间的转换非常快,因为它们基本上以相同的方式存储。

答案 1 :(得分:18)

convert(varchar(5), thedate, 108) between @leftTime and @rightTime

说明:

如果您有varchar(5),您将获得HH:mm

如果您varchar(8)获得HH:mm ss

108只获取SQL日期的时间

@leftTime@rightTime是两个要比较的变量

答案 2 :(得分:11)

如果您使用的是SQL Server 2008,则可以执行以下操作:

WHERE CONVERT(time(0), startHour) >= CONVERT(time(0), @startTime)

这是一个完整的测试:

DECLARE @tbEvents TABLE (
    timeEvent   int      IDENTITY,
    startHour   datetime
)

INSERT INTO @tbEvents (startHour) SELECT DATEADD(hh, 0, GETDATE())
INSERT INTO @tbEvents (startHour) SELECT DATEADD(hh, 1, GETDATE())
INSERT INTO @tbEvents (startHour) SELECT DATEADD(hh, 2, GETDATE())
INSERT INTO @tbEvents (startHour) SELECT DATEADD(hh, 3, GETDATE())
INSERT INTO @tbEvents (startHour) SELECT DATEADD(hh, 4, GETDATE())
INSERT INTO @tbEvents (startHour) SELECT DATEADD(hh, 5, GETDATE())

--SELECT * FROM @tbEvents

DECLARE @startTime  datetime

SET @startTime = DATEADD(mi, 65, GETDATE())

SELECT
    timeEvent,
    CONVERT(time(0), startHour)  AS 'startHour',
    CONVERT(time(0), @startTime) AS '@startTime'
FROM @tbEvents
WHERE CONVERT(time(0), startHour) >= CONVERT(time(0), @startTime)

答案 3 :(得分:4)

if (cast('2012-06-20 23:49:14.363' as time) between 
    cast('2012-06-20 23:49:14.363' as time) and 
    cast('2012-06-20 23:49:14.363' as time))

答案 4 :(得分:3)

只需将转换日期时间更改为应该执行操作的时间:

SELECT timeEvent 
FROM tbEvents 
WHERE convert(time, startHour) >= convert(time, @startHour)

答案 5 :(得分:3)

到目前为止我在解决方案中注意到的一个(可能很小的)问题是它们似乎都需要函数调用来处理比较。这意味着查询引擎将需要执行全表扫描以查找您之后的行 - 并且无法使用索引。如果表格不会变得特别大,这可能不会产生任何不利影响(你可以高兴地忽略这个答案)。

另一方面,如果表格变得非常大,查询的性能可能会受到影响。

我知道您声明您不希望比较日期部分 - 但是实际日期是存储在日期时间列中,还是您使用它来存储时间?如果是后者,您可以使用简单的比较运算符,这将减少CPU使用率,并允许查询引擎使用统计信息和索引(如果存在)来优化查询。

但是,如果使用datetime列来存储事件的日期和时间,这显然不起作用。在这种情况下,如果您可以修改应用程序和表结构,请将日期和时间分成两个单独的日期时间列,或者创建一个索引视图,选择源表的所有(相关)列,以及另一个包含该列的列。您希望搜索的时间元素(使用以前的任何答案来计算) - 并改变应用程序以查询视图。

答案 6 :(得分:3)

使用float不起作用。

DECLARE @t1 datetime, @t2 datetime
SELECT @t1 = '19000101 23:55:00', @t2 = '20001102 23:55:00'
SELECT CAST(@t1 as float) - floor(CAST(@t1 as float)), CAST(@t2 as float) - floor(CAST(@t2 as float))

您将看到值不相同(SQL Server 2005)。我想使用这种方法来检查午夜时间(完整方法有更多细节),其中我比较了23:55:00到00:05:00之间的当前时间。

答案 7 :(得分:2)

使用Datepart函数:DATEPART(datepart,date)

E.g#

  

SELECT DatePart(@YourVar,hh)* 60)+   DatePart(@YourVar,mi)* 60)

这将为您提供以分钟为单位的总时间,让您更轻松地进行比较。

如果您的日期相同,您可以使用DateDiff,否则您需要删除上述日期

答案 8 :(得分:2)

添加其他答案:

您可以创建一个从日期时间修剪日期的功能

CREATE FUNCTION dbo.f_trimdate (@dat datetime) RETURNS DATETIME AS BEGIN
    RETURN CONVERT(DATETIME, CONVERT(FLOAT, @dat) - CONVERT(INT, @dat))
END

所以这个:

DECLARE @dat DATETIME
SELECT @dat = '20080201 02:25:46.000'
SELECT dbo.f_trimdate(@dat)

会回来 1900-01-01 02:25:46.0​​00

答案 9 :(得分:2)

您可以创建两个datetime变量,并且只设置您需要比较的日期小时。

declare  @date1 datetime;
declare  @date2 datetime;

select   @date1 = CONVERT(varchar(20),CONVERT(datetime, '2011-02-11 08:00:00'), 114)
select   @date2 = CONVERT(varchar(20),GETDATE(), 114)

日期将是“1900-01-01”,你可以比较它

if @date1 <= @date2
   print '@date1 less then @date2'
else
   print '@date1 more then @date2'

答案 10 :(得分:1)

SELECT timeEvent 
  FROM tbEvents 
 WHERE CONVERT(VARCHAR,startHour,108) >= '01:01:01'

这告诉SQL Server使用样式108将当前日期/时间转换为varchar,即“hh:mm:ss”。您也可以替换'01:01:01',如果需要,可以替换另一个。

答案 11 :(得分:1)

我不喜欢依赖存储内部(日期时间是一个浮点数,整数=天和小数=时间),但我做的答案与答案Jhonny D. Cano相同。这就是我所知道的所有db开发者的方式。绝对不要转换为字符串。如果你必须避免处理float / int,那么最好的选择是使用DatePart()拉出小时/分钟/秒/毫秒

答案 12 :(得分:1)

我相信你想使用DATEPART('hour', datetime)

参考文献在这里:

http://msdn.microsoft.com/en-us/library/ms174420.aspx

答案 13 :(得分:0)

答案 14 :(得分:0)

我假设您的startHour列和@startHour变量都是DATETIME;在这种情况下,您应该转换为字符串:

SELECT timeEvent
FROM tbEvents
WHERE convert(VARCHAR(8), startHour, 8) >= convert(VARCHAR(8), @startHour, 8)

答案 15 :(得分:0)

@ronmurp提出了一个有效的问题 - 演员/场地方法同时返回不同的值。根据@littlechris的回答以及一个更通用的解决方案来解决具有分钟,秒,毫秒组件的时间,您可以使用此函数来计算从一天开始的毫秒数。

Create Function [dbo].[MsFromStartOfDay] ( @DateTime datetime )
Returns int
As
  Begin
      Return (
               ( Datepart( ms , @DateTime ) ) +
               ( Datepart( ss , @DateTime ) * 1000 ) +
               ( Datepart( mi , @DateTime ) * 1000 * 60 ) +
               ( Datepart( hh , @DateTime ) * 1000 * 60 * 60 )
              )
  End

我已经验证它在两个不同日期以相同的时间返回相同的int

declare @first datetime
set @first = '1900-01-01 23:59:39.090'
declare @second datetime
set @second = '2000-11-02 23:56:39.090'
Select dbo.MsFromStartOfDay( @first )
Select dbo.MsFromStartOfDay( @second )

此解决方案并不总是返回您期望的int。例如,在SQL 2005中尝试以下操作,它返回一个以'557'结尾的int而不是'556'。

set @first = '1900-01-01 23:59:39.556'
set @second = '2000-11-02 23:56:39.556'

我认为这与存储为float的DateTime的性质有关。不过,您仍然可以比较这两个数字。当我在使用DateTime.Now()在.NET中捕获的DateTime的“真实”数据集上使用这种方法并存储在SQL中时,我发现计算是准确的。

答案 16 :(得分:0)

TL; DR

如果要在搜索中使用索引,则将时间值与日期值分开(为提高性能,可能应该这样做)。您可以:(1)使用基于函数的索引,或者(2)仅为时间创建一个新列,对该列进行索引并在您的SELECT子句中使用它。


请紧记,如果您在SQL的WHERE子句中使用函数,您将失去任何索引性能的提升,引擎必须执行扫描搜索。只需使用EXPLAIN SELECT...运行查询即可确认。发生这种情况是因为引擎必须在字段中处理每个值以进行每次比较,并且转换后的值未编制索引。

大多数答案都说使用float()convert()cast()addtime()等。同样,如果您这样做,数据库将不使用索引。对于小桌子可能没问题。

尽管可以在WHERE参数中使用函数(where field = func(value)),因为您不会在表中更改每个字段的值。

如果要继续使用索引,可以为时间值创建基于函数的索引。正确的方法(及其支持)可能取决于您的数据库引擎。另一种选择是添加一列以仅存储时间值并为此列编制索引,但是请首先尝试使用前一种方法。


编辑06-02

在将数据库更新为具有新的时间列或使用索引的任何内容之前,请进行一些性能测试。在我的测试中,我发现性能提升很小(当我可以看到有所改善时),并且不值得添加新索引带来的麻烦和开销。