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.
Était-ce utile?

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

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