TSQL에서 두 날짜/시간이 동일한 날짜에 있는지 확인하는 좋은 방법은 무엇입니까?

StackOverflow https://stackoverflow.com/questions/22570

문제

내가 겪고 있는 문제는 다음과 같습니다.두 날짜가 같은 날인지 확인하기 위해 where 절의 날짜/시간을 비교해야 하는 대규모 쿼리가 있습니다.현재 짜증나는 해결책은 날짜/시간을 UDF로 보내서 같은 날 자정으로 변환한 다음 해당 날짜가 같은지 확인하는 것입니다.쿼리 계획의 경우 이는 조인 또는 where 절의 거의 모든 UDF와 마찬가지로 재앙입니다.이것은 내 애플리케이션에서 함수를 근절할 수 없고 쿼리 최적화 프로그램에 실제로 최상의 인덱스를 찾는 데 사용할 수 있는 것을 제공할 수 없는 유일한 장소 중 하나입니다.

이 경우 함수 코드를 쿼리에 다시 병합하는 것은 비실용적입니다.

나는 여기에 간단한 것을 놓치고 있다고 생각합니다.

참고할 수 있는 기능은 다음과 같습니다.

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

문제를 복잡하게 만들기 위해 시간대 테이블을 결합하여 행마다 다를 수 있는 현지 시간과 날짜를 확인합니다.

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

[편집하다]

@Todd의 제안을 통합하고 있습니다.

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

datediff가 어떻게 작동하는지에 대한 나의 오해(연속 연도의 같은 날은 예상한 대로 0이 아닌 366을 산출함)로 인해 많은 노력을 낭비하게 되었습니다.

그러나 쿼리 계획은 변경되지 않았습니다.나는 모든 것을 가지고 처음부터 다시 시작해야 한다고 생각합니다.

도움이 되었습니까?

해결책

이는 훨씬 더 간결합니다.

where 
  datediff(day, date1, date2) = 0

다른 팁

where 절의 왼쪽을 깨끗하게 유지해야 합니다.따라서 일반적으로 다음과 같은 작업을 수행합니다.

WHERE MyDateTime >= @activityDateMidnight 
      AND MyDateTime < (@activityDateMidnight + 1)

(일부 사람들은 대신 DATEADD(d, 1, @activityDateMidnight) 를 선호하지만 마찬가지입니다.)

TimeZone 테이블은 문제를 약간 복잡하게 만듭니다.스니펫에서는 약간 불분명하지만 t.TheDateInTable은 시간대 식별자를 사용하여 GMT에 있는 것처럼 보이며 현지 시간인 @activityDateMidnight와 비교하기 위해 오프셋을 추가하는 것 같습니다.하지만 ds.LocalTimeZone이 무엇인지 잘 모르겠습니다.

그렇다면 대신 @activityDateMidnight를 GMT로 가져와야 합니다.

where
year(date1) = year(date2)
and month(date1) = month(date2)
and day(date1) = day(date2)

꼭 읽어보세요 데이터베이스에서만 몇 줄의 코드를 변경하여 1000% + 개선을 얻을 수 있습니다. 날짜를 조작할 때 최적화 프로그램이 인덱스를 효과적으로 활용할 수 있다는 것을 확신할 수 있습니다.

에릭 Z 비어드:

나는 모든 날짜를 GMT로 저장합니다.사용 사례는 다음과 같습니다.1일 오후 11시(그리니치 표준시 기준)에 무슨 일이 일어났습니다.1일 활동도 보고 싶고, EST니까 밤 11시 활동도 보고 싶어요.원시 GMT 날짜 시간을 비교하면 내용을 놓칠 수 있습니다.보고서의 각 행은 다른 시간대의 활동을 나타낼 수 있습니다.

맞습니다. 하지만 2008년 1월 1일 EST의 활동에 관심이 있다고 말씀하신 경우:

SELECT @activityDateMidnight = '1/1/2008', @activityDateTZ = 'EST'

그냥 변환하면 돼 저것 GMT로(EST가 EDT로 이동하기 전날 또는 그 반대로 복잡한 쿼리를 무시합니다):

Table: TimeZone
Fields: TimeZone, Offset
Values: EST, -4

--Multiply by -1, since we're converting EST to GMT.
--Offsets are to go from GMT to EST.
SELECT @activityGmtBegin = DATEADD(hh, Offset * -1, @activityDateMidnight)
FROM TimeZone
WHERE TimeZone = @activityDateTZ

그러면 '2008년 1월 1일 오전 4시'가 표시됩니다.그런 다음 GMT로 검색할 수 있습니다.

SELECT * FROM EventTable
WHERE 
   EventTime >= @activityGmtBegin --1/1/2008 4:00 AM
   AND EventTime < (@activityGmtBegin + 1) --1/2/2008 4:00 AM

문제의 이벤트는 GMT EventTime인 2008년 1월 2일 오전 3시로 저장됩니다.EventTable에 TimeZone이 필요하지 않습니다(적어도 이 목적에서는).

EventTime은 함수에 포함되어 있지 않으므로 이는 직선 인덱스 스캔이므로 꽤 효율적입니다.EventTime을 클러스터형 인덱스로 만들면 성공할 것입니다.;)

개인적으로 저는 쿼리를 실행하기 전에 검색 시간을 GMT로 변환하는 앱을 사용하고 싶습니다.

이렇게 하면 날짜에서 시간 구성 요소가 제거됩니다.

select dateadd(d, datediff(d, 0, current_timestamp), 0)

여기에서는 옵션 측면에서 선택의 폭이 넓습니다.Sybase 또는 SQL Server 2008을 사용하는 경우 날짜 유형의 변수를 생성하고 여기에 날짜/시간 값을 할당할 수 있습니다.데이터베이스 엔진은 당신을 위해 시간을 없애줍니다.다음은 설명하기 위한 빠르고 더러운 테스트입니다(코드는 Sybase 방언으로 되어 있음).

declare @date1 date
declare @date2 date
set @date1='2008-1-1 10:00'
set @date2='2008-1-1 22:00'
if @date1=@date2
    print 'Equal'
else
    print 'Not equal'

SQL 2005 및 이전 버전의 경우 날짜를 시간 구성 요소가 없는 형식의 varchar로 변환할 수 있습니다.예를 들어 다음은 2008.08.22를 반환합니다.

select convert(varchar,'2008-08-22 18:11:14.133',102)

102 부분은 형식을 지정합니다(온라인 도서에는 사용 가능한 모든 형식이 나열되어 있습니다).

따라서 당신이 할 수 있는 일은 날짜/시간을 취하고 날짜 요소를 추출하고 시간을 버리는 함수를 작성하는 것입니다.다음과 같습니다:

create function MakeDate (@InputDate datetime) returns datetime as
begin
    return cast(convert(varchar,@InputDate,102) as datetime);
end

그런 다음 동반자 기능을 사용할 수 있습니다

Select * from Orders where dbo.MakeDate(OrderDate) = dbo.MakeDate(DeliveryDate)

에릭 Z 비어드:

활동 날짜는 현지 시간대를 나타내기 위한 것이지만 특정 시간대를 나타내지는 않습니다.

좋아요 - 드로잉 보드로 돌아갑니다.이 시도:

where t.TheDateINeedToCheck BETWEEN (
    dateadd(hh, (tz.Offset + ISNULL(ds.LocalTimeZone, 0)) * -1, @ActivityDate)
    AND
    dateadd(hh, (tz.Offset + ISNULL(ds.LocalTimeZone, 0)) * -1, (@ActivityDate + 1))
)

@ActivityDate를 현지 시간으로 변환하고 이를 비교합니다.이것이 인덱스를 사용할 수 있는 가장 좋은 기회입니다. 작동할지는 확실하지 않습니다. 인덱스를 사용해 보고 쿼리 계획을 확인해야 합니다.

다음 옵션은 인덱싱되고 계산된 TimeINeedToCheck가 포함된 인덱싱된 뷰입니다. 현지 시간으로.그런 다음 다음으로 돌아갑니다.

where v.TheLocalDateINeedToCheck BETWEEN @ActivityDate AND (@ActivityDate + 1)

INSERT 및 UPDATE에 약간의 오버헤드가 있지만 확실히 인덱스를 사용합니다.

datepart의 dayofyear 함수를 사용하겠습니다.


Select *
from mytable
where datepart(dy,date1) = datepart(dy,date2)
and
year(date1) = year(date2) --assuming you want the same year too

날짜 부분 참조 보기 여기.

시간대와 관련하여 모든 날짜를 단일 시간대(바람직하게는 UTC)에 저장해야 하는 또 다른 이유가 있습니다.어쨌든, 나는 datediff, datepart 및 다양한 내장 날짜 함수를 사용하는 답변이 최선의 선택이라고 생각합니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top