Ce qui est une bonne façon de vérifier si deux datetimes sont sur le même jour du calendrier de l'TSQL?
-
09-06-2019 - |
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.
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.