Vra

Hier is die kwessie ek met: Ek het 'n groot soektog wat nodig het om datetimes vergelyk in die waar klousule om te sien of twee datums is op dieselfde dag. My huidige oplossing, wat suig, is om die datetimes in 'n UDF om hulle te skakel na middernag van die dag, en dan kyk daardie datums vir gelykheid te stuur. Wanneer dit kom by die navraag plan, dit is 'n ramp, soos byna al UDFs in sluit of waar klousules. Dit is een van die enigste plekke in my program wat ek nie in staat was om uit te roei van die funksies en gee die navraag optimizer iets dit kan eintlik gebruik om die beste indeks op te spoor gewees het.

In hierdie geval, die samesmelting van die funksie-kode terug in die navraag blyk onprakties.

Ek dink ek mis iets eenvoudig hier.

Hier is die funksie vir verwysing.

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

Om sake nog ingewikkelder, ek aansluit op tydsone tafels aan die datum kyk teen die plaaslike tyd, wat anders vir elke ry kan wees:

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

[wysig]

Ek inkorporeer @ voorstel Todd se:

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

My wanopvatting oor hoe datediff werke (dieselfde dag van die jaar in agtereenvolgende jare oplewer 366, nie 0 as wat ek verwag het) het my 'n baie moeite te mors.

Maar die navraag plan het nie verander nie. Ek dink ek moet teruggaan na die tekenbord met die hele ding.

Was dit nuttig?

Oplossing

Dit is baie meer bondige:

where 
  datediff(day, date1, date2) = 0

Ander wenke

Jy pretty much het tot by die linkerkant van jou waar klousule skoon te hou. So, gewoonlik, jy wil iets doen:

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

(Sommige mense verkies DATEADD (d, 1, @activityDateMidnight) plaas - maar dit is dieselfde ding)

.

Die Tydsone tafel bemoeilik saak 'n bietjie al. Dit is 'n bietjie onduidelik uit jou brokkie, maar dit lyk asof t.TheDateInTable is in GMT met 'n Tydsone identifiseerder, en dat jy dan nou die toevoeging van die verreken te vergelyk teen @activityDateMidnight - wat is in plaaslike tyd. Ek is nie seker wat ds.LocalTimeZone is, al is.

As dit die geval is, dan moet jy @activityDateMidnight kry in GMT plaas.

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

Maak seker om te lees Slegs in 'n databasis kan jy 1000% + verbetering deur die verandering van 'n paar reëls van die kode sodat jy seker is dat die optimizer die indeks effektief kan benut wanneer geknoei met datums

Eric Z Baard:

  

Ek doen stoor alle datums in GMT. Hier is die gebruik geval: iets gebeur om 11:00 EST op die 1ste, wat is die 2de GMT. Ek wil aktiwiteit sien vir die 1ste, en ek is in EST so ek sal wil hê dat die 23:00 aktiwiteit sien. As ek net in vergelyking rou GMT datetimes, sou ek dinge mis. Elke ry in die verslag kan 'n aktiwiteit verteenwoordig van 'n ander tydsone.

Op die oomblik, maar as jy sê jy belangstel in aktiwiteit vir 1 Januarie 2008 EST is:

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

jy net nodig het om te sit dat om GMT (Ek ignoreer die komplikasie van die gebruik daarvan vir die dag voor EST gaan na EDT, of andersom):

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

wat jy '2008/01/01 04:00' moet gee. Dan kan jy net soek in GMT:

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

Die geleentheid betrokke is gestoor met 'n GMT EventTime van 2008/01/02 03:00. Jy hoef nie eens van die tydsone in die EventTable nodig (vir hierdie doel, ten minste).

Sedert EventTime is nie in 'n funksie, dit is 'n reguit indeks scan - wat mooi doeltreffende moet wees. Maak EventTime jou gegroepeer indeks, en dit sal vlieg. ;)

Persoonlik, sou ek die app sit die soektog tyd in GMT voordat jy die navraag.

Dit sal tyd komponent van 'n datum te verwyder vir jou:

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

Jy bederf vir keuse in terme van opsies hier. As jy met behulp van Sybase of SQL Server 2008 kan jy veranderlikes van tipe datum skep en wys hulle jou DATETIME waardes. Die databasis enjin ontslae raak van die tyd vir jou. Hier is 'n vinnige en vuil toets om te illustreer (Kode is in Sybase dialek):

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'

Vir SQL 2005 en vroeër wat jy kan doen is om te skakel die datum om 'n varchar in 'n formaat wat nie die tyd komponent. Byvoorbeeld die volgende opbrengste 2008/08/22

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

Die 102 deel spesifiseer die formaat (Boeke aanlyn kan 'n lys vir jou al die beskikbare formate)

So, wat jy kan doen is skryf 'n funksie wat 'n datum tyd neem en uittreksels van die datum element en terug gooi die tyd. Soos so:

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

Jy kan dan gebruik maak van die funksie vir metgeselle

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

Eric Z Baard:

  

die datum aktiwiteit bedoel is om die plaaslike tydsone dui, maar nie 'n spesifieke een

Goed - terug na die tekenbord. Probeer hierdie:

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))
)

wat die @ActivityDate plaaslike tyd sal vertaal, en vergelyk teen daardie. Dit is jou beste kans vir die gebruik van 'n indeks, maar ek is nie seker of dit sal werk -. Moet jy dit probeer en kyk die navraag plan

Die volgende opsie sou 'n geïndekseerde oog wees, met 'n geïndekseerde, bereken TimeINeedToCheck in plaaslike tyd . Dan kan jy net terug te gaan:

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

wat beslis sal gebruik maak van die indeks -. Al het jy 'n effense oorhoofse op INSERT en UPDATE dan

Ek sou die dayofyear funksie van datepart gebruik:


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

Sien die datepart verwysing hier .

Met betrekking tot tydsones, nog een meer rede om al die datums in 'n enkele tydsone (verkieslik UTC) te stoor. In elk geval, ek dink die antwoorde met behulp van datediff, datepart en die verskillende ingeboude datum funksies is jou beste bet.

Gelisensieer onder: CC-BY-SA met toeskrywing
Nie verbonde aan StackOverflow
scroll top