有什么方法可以检查两个日期时间是否在TSQL中的同一个日历日?

Eri*_*ard 23 sql t-sql sql-server datetime user-defined-functions

这是我遇到的问题:我有一个大型查询需要比较where子句中的日期时间,以查看两个日期是否在同一天.我当前的解决方案很糟糕,是将日期时间发送到UDF以将它们转换为同一天的午夜,然后检查这些日期是否相等.当涉及到查询计划时,这是一场灾难,几乎所有联接中的UDF或where子句都是如此.这是我的应用程序中唯一一个我无法根除函数并为查询优化器提供实际可用于查找最佳索引的地方之一.

在这种情况下,将函数代码合并回查询似乎是不切实际的.

我想我在这里缺少一些简单的东西.

这是参考功能.

if not exists (select * from dbo.sysobjects 
              where id = object_id(N'dbo.f_MakeDate') and               
              type in (N'FN', N'IF', N'TF', N'FS', N'FT'))
  exec('create function dbo.f_MakeDate() returns int as 
         begin declare @retval int return @retval end')
go

alter function dbo.f_MakeDate
(
    @Day datetime, 
    @Hour int, 
    @Minute int
)
returns datetime
as

/*

Creates a datetime using the year-month-day portion of @Day, and the 
@Hour and @Minute provided

*/

begin

declare @retval datetime
set @retval = cast(
    cast(datepart(m, @Day) as varchar(2)) + 
    '/' + 
    cast(datepart(d, @Day) as varchar(2)) + 
    '/' + 
    cast(datepart(yyyy, @Day) as varchar(4)) + 
    ' ' + 
    cast(@Hour as varchar(2)) + 
    ':' + 
    cast(@Minute as varchar(2)) as datetime)
return @retval
end

go
Run Code Online (Sandbox Code Playgroud)

更复杂的是,我正在加入时区表来检查当地时间的日期,每行可能会有所不同:

where 
dbo.f_MakeDate(dateadd(hh, tz.Offset + 
    case when ds.LocalTimeZone is not null 
    then 1 else 0 end, t.TheDateINeedToCheck), 0, 0) = @activityDateMidnight
Run Code Online (Sandbox Code Playgroud)

[编辑]

我正在纳入@Todd的建议:

where datediff(day, dateadd(hh, tz.Offset + 
    case when ds.LocalTimeZone is not null 
    then 1 else 0 end, t.TheDateINeedToCheck), @ActivityDate) = 0
Run Code Online (Sandbox Code Playgroud)

我对约瑟夫如何工作的误解(连续几年的同一天产生366,而不是我预期的0)导致我浪费了很多精力.

但查询计划没有改变.我想我需要回到整个事情的绘图板.

小智 62

这更简洁:

where 
  datediff(day, date1, date2) = 0
Run Code Online (Sandbox Code Playgroud)

  • 为什么这有9个上升票并获得答案?它很简洁,很好,并且导致OP走上了错误的道路.由于第二个日期@ActivityDate是固定的,我们可以将数学运算到右侧并获得更好的性能. (7认同)
  • 同意@emtucifor.这个逻辑不是sql server可以理解的. (2认同)
  • @Joyce,看看 [Mark Brackett 的回答](/sf/answers/1584831/)。他将“MyDateTime”放在任何数学运算符的一侧,*单独*。这意味着查询引擎可以执行**范围查找**并直接在 b 树中导航到两次之间的行。有了这个可怕的、可怕的、可怕的建议,*表格中的每一行*都必须对其进行数学运算,这是一次**扫描**,对性能来说是可怕的。如果表有 1 亿行,而只有 10 行符合条件,想想看所有行找到 10 行有多糟糕!!!!!!! (2认同)

Mar*_*ett 19

你几乎必须保持where子句的左侧清洁.所以,通常情况下,你会这样做:

WHERE MyDateTime >= @activityDateMidnight 
      AND MyDateTime < (@activityDateMidnight + 1)
Run Code Online (Sandbox Code Playgroud)

(有些人更喜欢DATEADD(d,1,@ activityDateMidnight) - 但这是相同的事情).

TimeZone表虽然有点复杂.你的片段有点不清楚,但它看起来像是.DateInTable在GMT中带有时区标识符,然后你要添加偏移量来与@activityDateMidnight进行比较 - 这是当地时间.不过,我不确定ds.LocalTimeZone是什么.

如果是这种情况,那么你需要将@activityDateMidnight改为GMT.


jas*_*ldo 5

where
year(date1) = year(date2)
and month(date1) = month(date2)
and day(date1) = day(date2)
Run Code Online (Sandbox Code Playgroud)