Ce qui est une bonne façon de vérifier si deux datetimes sont sur le même jour du calendrier de l'TSQL?

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

Question

Voici la question que je vais avoir:J'ai un grand requête que les besoins de comparer datetimes dans la clause where pour voir si les deux dates sont sur le même jour.Ma solution actuelle, qui aspire, est d'envoyer la datetimes dans un UDF de les convertir à minuit le même jour, et vérifiez les dates pour l'égalité.Quand il s'agit du plan de requête, c'est une catastrophe, comme le sont presque toutes les fonctions définies par l'utilisateur dans les jointures ou les clauses where.C'est l'un des seuls endroits dans mon application que je n'ai pas été capable d'éradiquer les fonctions et donner l'optimiseur de requête quelque chose qu'il peut utiliser pour localiser le meilleur indice.

Dans ce cas, la fusion, le code de fonction de retour dans la requête ne semble pas réaliste.

Je pense que je suis absent quelque chose de simple ici.

Voici la fonction de référence.

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

Pour compliquer les choses, je vais rejoindre sur les tables de fuseau horaire pour vérifier la date à contre le, heure locale, ce qui peut être différent pour chaque ligne:

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

[Modifier]

Je suis intégrant @Todd suggestion:

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

Mon idée fausse au sujet de la façon dont datediff œuvres (le même jour de l'année en années consécutives rendements 366, pas 0 comme je m'y attendais) m'a fait perdre beaucoup d'effort.

Mais le plan de requête n'a pas changé.Je pense que j'ai besoin de revenir à la planche à dessin avec l'ensemble de la chose.

Était-ce utile?

La solution

C'est beaucoup plus concise:

where 
  datediff(day, date1, date2) = 0

Autres conseils

Vous devez garder le côté gauche de votre clause where propre.Donc, normalement, vous auriez du faire quelque chose comme:

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

(Certaines personnes préfèrent DATEADD(d, 1, @activityDateMidnight) au lieu - mais c'est la même chose).

Le Fuseau horaire de table complique la question, un peu cependant.C'est un peu moins clair, à partir de votre extrait de code, mais il ressemble t.TheDateInTable est en GMT avec un Temps identificateur de la Zone, et que vous êtes alors en ajoutant le décalage à comparer @activityDateMidnight - qui est en heure locale.Je ne suis pas sûr de ce que la ds.LocalTimeZone est, bien.

Si c'est le cas, alors vous avez besoin pour obtenir de l' @activityDateMidnight en GMT à la place.

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

Assurez-vous de lire Seulement Dans Une Base De Données Vous Pouvez Obtenir 1000% + Amélioration En Changeant Quelques Lignes De Code de sorte que vous êtes sûr que l'optimiseur peut utiliser l'indice efficacement lorsque déconner avec les dates

Eric Z Barbe:

Je ne stocker toutes les dates de GMT.Voici les cas d'utilisation:quelque chose s'est passé à 11:00 PM (heure de l'est le 1er, qui est le 2ème GMT.Je veux voir de l'activité pour le 1er, et je suis dans l'est, donc je voudrai voir la 11PM activité.Si j'ai juste comparé raw GMT datetimes, je ne manquerais choses.Chaque ligne dans le rapport peuvent représenter une activité d'un autre fuseau horaire.

Droit, mais quand vous dites que vous êtes intéressé à l'activité pour le 1er janvier 2008 est:

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

vous avez juste besoin de les convertir que à l'heure GMT (je suis ignorant la complication de l'interrogation de la journée avant de HNE va à l'EDT, ou vice-versa):

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

qui devrait vous donner '1/1/2008 4:00 AM".Ensuite, vous pouvez simplement recherche en GMT:

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

L'événement en question est stocké avec un GMT EventTime de 1/2/2008 3:00 AM.Vous n'avez même pas besoin de le Fuseau horaire dans les EventTable (à cet effet, au moins).

Depuis EventTime n'est pas une fonction, c'est un droit de l'indice de scan qui devrait être assez efficace.Faire EventTime votre index cluster, et il volera.;)

Personnellement, j'aurais l'application de convertir le temps de recherche en GMT, avant d'exécuter la requête.

cela permettra d'éliminer le temps de composants à partir d'une date pour vous:

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

Vous avez l'embarras du choix en termes d'options ici.Si vous utilisez Sybase et SQL Server 2008, vous pouvez créer des variables de type date et l'assignation des valeurs datetime.Le moteur de base de données se débarrasse du temps pour vous.Voici un moyen rapide et sale de test pour illustrer (le Code est dans Sybase dialecte):

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'

Pour SQL 2005 et antérieures de ce que vous pouvez faire est de convertir la date varchar dans un format qui ne ont pas le temps de composant.Pour l'exemple suivant renvoie 2008.08.22

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

102 partie spécifie la mise en forme (la documentation en ligne de pouvez liste pour vous tous les formats disponibles)

Donc, ce que vous pouvez faire est d'écrire une fonction qui prend un datetime et des extraits de l'élément de date et ignore le temps.Comme suit:

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

Vous pouvez ensuite utiliser la fonction pour les compagnons

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

Eric Z Barbe:

la date de l'activité a pour but d'indiquer le fuseau horaire local, mais pas un seul

Bon retour à la planche à dessin.Essayez ceci:

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

qui va traduire le @ActivityDate à l'heure locale, et comparez-les avec que.C'est votre meilleure chance pour l'utilisation d'un index, mais je ne suis pas sûr que ce soit au travail, vous devriez essayer et vérifier le plan de requête.

La prochaine option serait une vue indexée, avec un indexée, calculée TimeINeedToCheck à l'heure locale.Ensuite, vous avez juste retourner à:

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

qui seraient certainement utiliser l'indice - si vous avez une légère surcharge sur INSÉRER et mettre à JOUR ensuite.

Je voudrais utiliser le dayofyear fonction datepart:


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

Voir la datepart de référence ici.

Concernant les fuseaux horaires, encore une raison de plus pour stocker toutes les dates dans un seul fuseau horaire (de préférence UTC).De toute façon, je pense que les réponses à l'aide de datediff, datepart et les différents intégré dans les fonctions de date sont votre meilleur pari.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top