Domanda

Qui è il problema che sto avendo:Ho una query di grandi dimensioni che ha bisogno di confrontare datetimes nella clausola where per vedere se le due date sono lo stesso giorno.La mia soluzione attuale, che fa schifo, è quello di inviare il datetimes in una UDF per convertirli alla mezzanotte del giorno stesso, e quindi controllare le date per la parità.Quando si tratta di il piano di query, questo è un disastro, come quasi tutte le Udf in entrata o in cui le clausole.Questo è uno dei pochi posti nella mia applicazione che non sono stato in grado di sradicare le funzioni e dare il query optimizer qualcosa che si può effettivamente utilizzare per individuare il miglior indice.

In questo caso, la fusione, la funzione di codice nella query sembra impraticabile.

Penso che mi manca qualcosa di semplice qui.

Ecco la funzione per riferimento.

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

A complicare le cose, io sono di entrare fuso orario tabelle per controllare la data contro il tempo locale, che può essere diverso per ogni riga:

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

[Modifica]

Sto incorporando @Todd suggerimento:

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

La mia idea sbagliata su come datediff funziona (lo stesso giorno dell'anno in anni consecutivi produce 366, e non 0 come mi aspettavo) mi ha fatto perdere un sacco di fatica.

Ma il piano di query non cambia.Penso che ho bisogno di tornare al tavolo da disegno con il tutto.

È stato utile?

Soluzione

Questo è molto più conciso:

where 
  datediff(day, date1, date2) = 0

Altri suggerimenti

È praticamente necessario per mantenere il lato sinistro della clausola where pulito.Quindi, normalmente, devi fare qualcosa di simile:

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

(Alcune persone preferiscono DATEADD(d, 1, @activityDateMidnight) invece - ma è la stessa cosa).

Il Fuso orario tabella complica la questione un po ' però.E 'un po' poco chiaro dal tuo snippet, ma sembra che t.TheDateInTable è GMT con un fuso Orario identificatore, e che si sta aggiungendo poi l'offset da confrontare, @activityDateMidnight - che è in ora locale.Io non sono sicuro di quello ds.LocalTimeZone è, però.

Se questo è il caso, allora avete bisogno di ottenere @activityDateMidnight in GMT, invece.

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

Assicurarsi di leggere Solo In Un Database, Si Può Ottenere 1000% + Miglioramento Cambiando Un Paio Di Righe Di Codice così sei sicuro che l'ottimizzatore può utilizzare l'indice in modo efficace quando fare confusione con le date

Eric Z Barba:

Memorizzare tutte le date in GMT.Ecco il caso d'uso:è successo qualcosa a 11:00 PM EST il 1 ° che il 2 ° GMT.Voglio vedere di attività per il 1°, e io sono in EST così mi verrà la voglia di vedere il 11 attività.Se ho solo rispetto raw GMT datetimes, vorrei mancare le cose.Ogni riga del report può rappresentare un'attività da un fuso orario diverso.

Giusto, ma quando si dicono siete interessati in attività dal 1 gennaio 2008 EST:

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

hai solo bisogno di convertire che GMT (sto ignorando la complicazione di una query per il giorno prima di EST va a EDT, o viceversa):

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

che dovrebbe dare '1/1/2008 4:00 AM'.Quindi, si può solo cercare nelle GMT:

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

L'evento in questione è archiviata con una GMT base all'ora dell'evento di 1/2/2008 3:00 AM.Non è nemmeno necessario il Fuso orario in EventTable (per questo scopo, almeno).

Dal base all'ora dell'evento non è in funzione, questo è un semplice indice di scansione che dovrebbe essere abbastanza efficiente.Fare in base all'ora dell'evento indice cluster, e si metterà a volare.;)

Personalmente, mi piacerebbe avere l'app di convertire il tempo di ricerca in GMT, prima di eseguire la query.

questo rimuoverà la componente tempo da una data:

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

Avrete l'imbarazzo della scelta in termini di opzioni qui.Se si utilizza Sybase SQL Server o SQL Server 2008, è possibile creare variabili di tipo data e assegnare loro i tuoi valori datetime.Il motore di database si sbarazza del tempo per voi.Ecco una breve e veloce test per illustrare (il Codice è in Sybase dialetto):

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'

Per SQL 2005 e la prima cosa che puoi fare è convertire la data in un campo varchar in un formato che non hanno la componente tempo.Per esempio seguente restituisce 2008.08.22

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

102 parte di specificare la formattazione (Libri online elenco di tutti i formati disponibili)

Così, cosa si può fare è scrivere una funzione che prende un valore di tipo datetime e estrae l'elemento di data e annulla il tempo.In questo modo:

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

È quindi possibile utilizzare la funzione per i compagni

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

Eric Z Barba:

la data dell'attività è la funzione di indicare il fuso orario locale, ma non uno in particolare

Va bene - torna al tavolo da disegno.Prova questo:

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

che permetterà di tradurre il @ActivityDate ora locale, e confrontare i contro che.Che è la tua migliore possibilità di utilizzo di un indice, anche se non sono sicuro che lavorerò - si dovrebbe provare e verificare il piano di query.

L'opzione successiva sarebbe una vista indicizzata, con un indicizzati, calcolata TimeINeedToCheck ora locale.Poi basta andare indietro a:

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

che sarebbe sicuramente utilizzare l'indice - se si dispone di un piccolo sovraccarico sull'INSERIMENTO e l'AGGIORNAMENTO dei poi.

Vorrei utilizzare il dayofyear funzione datepart:


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

Vedere la datepart di riferimento qui.

Per quanto riguarda i fusi orari, ancora un motivo in più per memorizzare tutte le date in un unico fuso orario (preferibilmente UTC).Comunque, penso che le risposte utilizzando la funzione datediff, datepart e le diverse built-in funzioni di data sono la vostra scommessa migliore.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top