Comment faire pour supprimer la partie heure d'une valeur datetime (SQL Server)?
-
08-06-2019 - |
Question
Voici ce que j'utilise:
SELECT CAST(FLOOR(CAST(getdate() as FLOAT)) as DATETIME)
Je suis en train de penser, il peut être mieux et de façon plus élégante.
Exigences:
- Il doit être aussi rapide que possible (moins de casting, le mieux).
- Le résultat final doit être une
datetime
type, pas une chaîne de caractères.
La solution
SQL Server 2008 et jusqu'
Dans SQL Server 2008 et, bien sûr, le moyen le plus rapide est Convert(date, @date)
.Cela peut être jeté en arrière pour un datetime
ou datetime2
si nécessaire.
Ce qui Est Vraiment le Meilleur Dans SQL Server 2005 et plus?
J'ai vu incompatible réclamations au sujet de ce qui est le plus rapide pour tronquer le temps à partir d'une date en SQL Server, et certaines personnes ont même dit qu'ils n'en test, mais mon expérience a été différente.Donc, nous allons faire plus de tests rigoureux et de laisser tout le monde avoir le script donc si je le fais toutes les erreurs que les gens peuvent me corriger.
Flotter Les Conversions Ne Sont Pas Exacts
Tout d'abord, je voudrais rester à l'écart de conversion datetime
pour float
, parce qu'il ne convertit pas correctement.Vous pouvez vous en tirer avec faire le temps à l'enlèvement de la chose avec précision, mais je pense que c'est une mauvaise idée de l'utiliser parce qu'il implicitement communique aux développeurs que c'est un fonctionnement sûr et il n'est pas.Prendre un coup d'oeil:
declare @d datetime;
set @d = '2010-09-12 00:00:00.003';
select Convert(datetime, Convert(float, @d));
-- result: 2010-09-12 00:00:00.000 -- oops
Ce n'est pas quelque chose que nous devrions enseigner les gens dans notre code ou dans nos exemples en ligne.
Aussi, il n'est même pas le moyen le plus rapide!
La Preuve Des Tests De Performance
Si vous souhaitez effectuer quelques tests pour voir comment les différentes méthodes de vraiment faire de la pile en place, alors vous aurez besoin de ce script d'installation pour exécuter les tests plus loin vers le bas:
create table AllDay (Tm datetime NOT NULL CONSTRAINT PK_AllDay PRIMARY KEY CLUSTERED);
declare @d datetime;
set @d = DateDiff(Day, 0, GetDate());
insert AllDay select @d;
while @@ROWCOUNT != 0
insert AllDay
select * from (
select Tm =
DateAdd(ms, (select Max(DateDiff(ms, @d, Tm)) from AllDay) + 3, Tm)
from AllDay
) X
where Tm < DateAdd(Day, 1, @d);
exec sp_spaceused AllDay; -- 25,920,000 rows
Veuillez noter que cela crée un 427.57 MB table dans votre base de données et prendre quelque chose comme 15 à 30 minutes pour s'exécuter.Si votre base de données est petit et fixé à 10% de croissance, il faudra plus de temps que si vous taille assez grande première.
Maintenant pour la performance réelle script de test.Veuillez noter qu'il est déterminé à ne pas retourner les lignes vers le client, comme c'est fou cher sur 26 millions de lignes et permettrait de masquer les différences de performances entre les méthodes.
Résultats De La Performance
set statistics time on;
-- (All queries are the same on io: logical reads 54712)
GO
declare
@dd date,
@d datetime,
@di int,
@df float,
@dv varchar(10);
-- Round trip back to datetime
select @d = CONVERT(date, Tm) from AllDay; -- CPU time = 21234 ms, elapsed time = 22301 ms.
select @d = CAST(Tm - 0.50000004 AS int) from AllDay; -- CPU = 23031 ms, elapsed = 24091 ms.
select @d = DATEDIFF(DAY, 0, Tm) from AllDay; -- CPU = 23782 ms, elapsed = 24818 ms.
select @d = FLOOR(CAST(Tm as float)) from AllDay; -- CPU = 36891 ms, elapsed = 38414 ms.
select @d = CONVERT(VARCHAR(8), Tm, 112) from AllDay; -- CPU = 102984 ms, elapsed = 109897 ms.
select @d = CONVERT(CHAR(8), Tm, 112) from AllDay; -- CPU = 103390 ms, elapsed = 108236 ms.
select @d = CONVERT(VARCHAR(10), Tm, 101) from AllDay; -- CPU = 123375 ms, elapsed = 135179 ms.
-- Only to another type but not back
select @dd = Tm from AllDay; -- CPU time = 19891 ms, elapsed time = 20937 ms.
select @di = CAST(Tm - 0.50000004 AS int) from AllDay; -- CPU = 21453 ms, elapsed = 23079 ms.
select @di = DATEDIFF(DAY, 0, Tm) from AllDay; -- CPU = 23218 ms, elapsed = 24700 ms
select @df = FLOOR(CAST(Tm as float)) from AllDay; -- CPU = 29312 ms, elapsed = 31101 ms.
select @dv = CONVERT(VARCHAR(8), Tm, 112) from AllDay; -- CPU = 64016 ms, elapsed = 67815 ms.
select @dv = CONVERT(CHAR(8), Tm, 112) from AllDay; -- CPU = 64297 ms, elapsed = 67987 ms.
select @dv = CONVERT(VARCHAR(10), Tm, 101) from AllDay; -- CPU = 65609 ms, elapsed = 68173 ms.
GO
set statistics time off;
Certaines Randonnées Analyse
Quelques notes à ce sujet.Tout d'abord, si l'exécution d'un GROUPE ou PAR une comparaison, il n'y a pas besoin de convertir le retour à datetime
.Ainsi, vous pouvez économiser de l'UC en évitant que, sauf si vous avez besoin de la valeur finale à des fins d'affichage.Vous pouvez même GROUPE PAR les données de la valeur et de mettre de la conversion que dans la clause SELECT:
select Convert(datetime, DateDiff(dd, 0, Tm))
from (select '2010-09-12 00:00:00.003') X (Tm)
group by DateDiff(dd, 0, Tm)
Aussi, voir comment les conversions numériques seulement de prendre un peu plus de temps pour revenir à datetime
, mais l' varchar
la conversion est presque le double?Cela révèle la partie du PROCESSEUR qui est dévoué à la date de calcul dans les requêtes.Il y a des parties de l'utilisation de l'UC qui ne comportent pas de date de calcul, et cela semble être quelque chose de proche de 19875 ms au-dessus des requêtes.Ensuite, la conversion des sommes supplémentaires, donc si il y a deux conversions, ce montant est utilisé jusqu'à environ deux fois.
Un examen plus approfondi révèle que, par rapport à Convert(, 112)
, le Convert(, 101)
la requête a d'autres CPU frais (car il utilise un plus varchar
?), parce que le deuxième cours de conversion vers date
n'est pas le prix de conversion initial de varchar
, mais avec Convert(, 112)
il est plus proche de la même 20000 ms CPU coût de base.
Voici ces calculs sur le temps CPU que j'ai utilisé pour l'analyse ci-dessus:
method round single base
----------- ------ ------ -----
date 21324 19891 18458
int 23031 21453 19875
datediff 23782 23218 22654
float 36891 29312 21733
varchar-112 102984 64016 25048
varchar-101 123375 65609 7843
ronde est le temps de calcul pour un aller et retour en arrière pour
datetime
.unique est de temps PROCESSEUR pour une conversion unique pour l'autre type de données (celle qui est la conséquence de la suppression de la partie heure).
la base de le calcul de la soustraction de
single
la différence entre les deux invocations:single - (round - single)
.C'est un ballpark figure qui suppose la conversion vers et à partir de ce type de données etdatetime
est approximativement le même dans les deux sens.Il semble que cette hypothèse n'est pas parfait, mais est proche car les valeurs sont tout près de 20000 ms avec une seule exception.
Une chose plus intéressante est que le coût de base est presque égale à la seule Convert(date)
méthode (qui a presque 0 coût, comme le serveur interne de l'extrait de l'entier partie jour dès la sortie des quatre premiers octets de l' datetime
type de données).
Conclusion
Donc à quoi il ressemble, c'est que la seule direction varchar
méthode de conversion prend environ 1,8 µs et la seule direction DateDiff
méthode prend environ 0.18 µs.Je suis en se fondant sur la plus conservatrice "de la base de CPU" le temps dans mes tests de 18458 ms total pour 25,920,000 lignes, 23218 ms / 25920000 = 0.18 µs.L'apparente 10x amélioration semble beaucoup, mais c'est franchement assez petit jusqu'à ce que vous traitez avec des centaines de milliers de lignes (617 k lignes = 1 seconde d'épargne).
Même compte tenu de cette petite absolue amélioration, à mon avis, l' DateAdd
méthode l'emporte, car c'est la meilleure combinaison de performance et de clarté.La réponse qui nécessite un "nombre magique" de 0.50000004
va mordre quelqu'un un jour (cinq zéros ou six???), en plus, c'est plus difficile à comprendre.
Notes Supplémentaires
Quand j'ai un peu de temps, je vais changer 0.50000004
pour '12:00:00.003'
et de voir comment il le fait.Il est converti à la même datetime
valeur et je trouve ça beaucoup plus facile à retenir.
Pour ceux qui sont intéressés, les tests ci-dessus ont été exécutés sur un serveur où @@Version renvoie les éléments suivants:
Microsoft SQL Server 2008 (RTM) - 10.0.1600.22 (Intel X86) 9 Juillet 2008 À 14:43:34 Copyright (c) de 1988 à 2008 Microsoft Corporation Édition Standard sur Windows NT 5.2 (Build 3790:Service Pack 2)
Autres conseils
SQL Server 2008 est une nouvelle date type de données et cela simplifie ce problème:
SELECT CAST(CAST(GETDATE() AS date) AS datetime)
Itzik Ben-Gan en DATETIME Calculs, Partie 1 (SQL Server Magazine, février 2007) montre trois méthodes pour effectuer une telle conversion (plus lent au plus rapide;la différence entre la deuxième et la troisième méthode est de petite taille):
SELECT CAST(CONVERT(char(8), GETDATE(), 112) AS datetime)
SELECT DATEADD(day, DATEDIFF(day, 0, GETDATE()), 0)
SELECT CAST(CAST(GETDATE() - 0.50000004 AS int) AS datetime)
Votre technique (coulée de flotteur) est suggéré par un lecteur dans le numéro d'avril du magazine.Selon lui, il a une performance comparable à celle de la deuxième technique présentée ci-dessus.
Votre CAST
-FLOOR
-CAST
semble déjà être de façon optimale, au moins sur MS SQL Server 2005.
Certaines autres solutions que j'ai vu ont une chaîne de conversion, comme Select Convert(varchar(11), getdate(),101)
en eux, qui est plus lent par un facteur de 10.
S'il vous plaît essayer:
SELECT CONVERT(VARCHAR(10),[YOUR COLUMN NAME],105) [YOURTABLENAME]
SQL2005:Je recommande de distribution au lieu de dateadd.Par exemple,
select cast(DATEDIFF(DAY, 0, datetimefield) as datetime)
en moyenne autour de 10% plus rapide sur mon dataset, que
select DATEADD(DAY, DATEDIFF(DAY, 0, datetimefield), 0)
(et coulée dans smalldatetime a été encore plus rapide)