Comment puis-je tronquer une date / heure dans SQL Server?

281

Quelle est la meilleure façon de tronquer une valeur datetime (pour supprimer les heures minutes et secondes) dans SQL Server 2008?

Par exemple:

declare @SomeDate datetime = '2009-05-28 16:30:22'
select trunc_date(@SomeDate)

-----------------------
2009-05-28 00:00:00.000
Jules César
la source

Réponses:

495

Cela continue de recueillir fréquemment des votes supplémentaires, même plusieurs années plus tard, et je dois donc le mettre à jour pour les versions modernes de Sql Server. Pour Sql Server 2008 et versions ultérieures, c'est simple:

cast(getDate() As Date)

Notez que les trois derniers paragraphes près du bas s'appliquent toujours, et vous devez souvent prendre du recul et trouver un moyen d'éviter la distribution en premier lieu.

Mais il existe également d'autres moyens d'y parvenir. Voici les plus courants.

La bonne façon (nouveau depuis Sql Server 2008):

cast(getdate() As Date)

La bonne façon (ancienne):

dateadd(dd, datediff(dd,0, getDate()), 0)

C'est plus ancien maintenant, mais cela vaut toujours la peine d'être connu, car il peut également s'adapter facilement à d'autres moments, comme le premier moment du mois, des minutes, des heures ou de l'année.

Cette manière correcte utilise des fonctions documentées qui font partie de la norme ansi et sont garanties de fonctionner, mais cela peut être un peu plus lent. Il fonctionne en recherchant combien de jours il y a du jour 0 au jour actuel, et en ajoutant ce nombre de jours au jour 0. Il fonctionnera quelle que soit la façon dont votre datetime est stockée et quels que soient vos paramètres régionaux.

La voie rapide:

cast(floor(cast(getdate() as float)) as datetime)

Cela fonctionne car les colonnes datetime sont stockées en tant que valeurs binaires de 8 octets. Castez-les pour flotter, fixez-les au sol pour supprimer la fraction et la partie temporelle des valeurs disparaît lorsque vous les restituez à datetime. C'est juste un peu de décalage sans logique compliquée et c'est très rapide.

Sachez que cela repose sur un détail d'implémentation que Microsoft est libre de modifier à tout moment, même dans une mise à jour de service automatique. Ce n'est pas non plus très portable. Dans la pratique, il est très peu probable que cette implémentation change de sitôt, mais il est toujours important d'être conscient du danger si vous choisissez de l'utiliser. Et maintenant que nous avons la possibilité de caster comme date, c'est rarement nécessaire.

La mauvaise direction:

cast(convert(char(11), getdate(), 113) as datetime)

La mauvaise façon fonctionne en convertissant en chaîne, en tronquant la chaîne et en reconvertissant en date / heure. C'est faux , pour deux raisons: 1) cela pourrait ne pas fonctionner dans tous les environnements locaux et 2) il s'agit de la manière la plus lente de le faire ... et pas seulement un peu; c'est comme un ordre de grandeur ou deux plus lent que les autres options.


Mise à jour Cela a obtenu des votes récemment, et je veux donc ajouter que depuis que j'ai posté cela, j'ai vu des preuves assez solides que Sql Server optimisera la différence de performances entre la manière "correcte" et la méthode "rapide" , ce qui signifie que vous devriez maintenant privilégier le premier.

Dans les deux cas, vous souhaitez écrire vos requêtes pour éviter d'avoir à le faire en premier lieu . Il est très rare que vous effectuiez ce travail sur la base de données.

Dans la plupart des endroits, la base de données est déjà votre goulot d'étranglement. C'est généralement le serveur qui est le plus cher à ajouter du matériel pour améliorer les performances et le plus difficile à faire pour que ces ajouts soient corrects (vous devez équilibrer les disques avec la mémoire, par exemple). C'est aussi le plus difficile à étendre vers l'extérieur, à la fois techniquement et d'un point de vue commercial; techniquement, il est beaucoup plus facile d'ajouter un serveur Web ou d'application qu'un serveur de base de données et même si c'était faux, vous ne payez pas 20 000 $ + par licence de serveur pour IIS ou apache.

Le point que j'essaie de faire est que chaque fois que possible, vous devriez faire ce travail au niveau de l'application. La seule fois où vous devriez vous retrouver à tronquer une date / heure sur Sql Server est lorsque vous devez effectuer un regroupement par jour, et même dans ce cas, vous devriez probablement avoir une colonne supplémentaire configurée en tant que colonne calculée, maintenue au moment de l'insertion / mise à jour ou maintenue. dans la logique d'application. Obtenez ce travail révolutionnaire et gourmand en CPU de votre base de données.

Joel Coehoorn
la source
6
la "voie rapide" est toujours la voie la plus rapide pour sql 2008 selon un benchmark que je viens de courir
Sam Saffron
3
Pour info: stackoverflow.com/q/1177449/27535 et stackoverflow.com/q/133081/27535 Le dateadd / datiff "gagne ...". Pour une seule variable, peu importe, et on espère que vous avez calculé des colonnes ou plus sur un million de lignes :-)
gbn
9
Cette méthode "correcte" ne fonctionne qu'accidentellement. La façon dont il est écrit est comme si la syntaxe de DateAdd était (intervalle, date, incrément), mais ce n'est pas le cas. C'est (intervalle, incrément, date). Je suis tombé dessus lorsque j'ai essayé de tronquer une date au premier du mois: SELECT DATEADD (m, 0, DATEDIFF (m, 0, GETDATE ())) ne fonctionne pas, mais SELECT DATEADD (m, DATEDIFF (m, 0, GETDATE ()), 0) le fait. Du moins, c'est ce que je vois dans 2008R2.
Kelly Cline
1
@Kelly en 2008R2, pourquoi pas juste cast(getdate() as date)?
Joel Coehoorn
2
Ils travaillent tous sur une colonne datetime. getdate()voici un remplaçant pour n'importe quelle source datetime que vous pourriez avoir.
Joel Coehoorn
44

Pour SQL Server 2008 uniquement

CAST(@SomeDateTime AS Date) 

Ensuite, remettez-le en datetime si vous le souhaitez

CAST(CAST(@SomeDateTime AS Date) As datetime)
DJ.
la source
Bon point: je suis toujours sur 2005 et donc pour 2008, c'est probablement la nouvelle méthode "correcte" et peut même correspondre aux performances de la méthode "rapide".
Joel Coehoorn
1
Les performances de cette nouvelle méthode sont encore plus rapides que la méthode "rapide".
ErikE
21

Juste pour une réponse plus complète, voici une méthode de travail pour tronquer n'importe laquelle des parties de la date et y compris les minutes (remplacer GETDATE()par la date à tronquer).

Ceci est différent de la réponse acceptée en ce que vous pouvez utiliser non seulement dd(jours), mais n'importe quelle partie de date (voir ici ):

dateadd(minute, datediff(minute, 0, GETDATE()), 0)

Notez que dans l'expression ci-dessus, la 0est une date constante au début d'une année (1900-01-01). Si vous devez tronquer des parties plus petites, telles que des secondes ou des millisecondes, vous devez prendre une date constante qui est plus proche de la date à tronquer pour éviter un débordement.

Lucero
la source
1
C'était monstrueusement utile. J'ai cherché partout un moyen de tronquer la date-heure à un endroit inférieur à la journée complète.
Michael - Où est Clay Shirky le
1
@Michael, merci pour les commentaires, bon de savoir que cela vous a aidé!
Lucero
1
+1 cela devrait avoir plus de votes positifs, c'est une excellente réponse qui se développe sur la réponse sélectionnée.
2014
1
Juste pour que l'Internet sache, vous n'avez pas à être limité à des périodes partielles complètes. Voici un exemple pour des intervalles de 15 minutes, en utilisant la division entière:dateadd(minute, datediff(minute, 0, GETDATE()) / 15 * 15, 0)
Michael - Où est Clay Shirky
7

L'extrait que j'ai trouvé sur le Web lorsque j'ai dû le faire était:

 dateadd(dd,0, datediff(dd,0, YOURDATE))
 e.g.
 dateadd(dd,0, datediff(dd,0, getDate()))
Tom Ritter
la source
Je suis en 2005, mais je pensais que 2008 avait une nouvelle fonction pour ça ??
KM.
2
Soigné! J'aurais eu recours à la séparation des parties de date et à l'utilisation de la gestion des chaînes pour les remonter. Peut ne pas être pertinent, mais SQL2008 a un type de données purement date sans élément time.
Frans
1
Et notez que vous avez mélangé les opérandes DateAdd, c'est DateAdd(dd, DateDiff(...), 0). Cela peut vous mordre si vous ne faites pas attention.
ErikE
1

Dans SQl 2005, votre fonction trunc_date pouvait être écrite comme ceci.

(1)

CREATE FUNCTION trunc_date(@date DATETIME)
RETURNS DATETIME
AS
BEGIN
    CAST(FLOOR( CAST( @date AS FLOAT ) )AS DATETIME)
END

La première méthode est beaucoup plus propre. Il utilise seulement 3 appels de méthode, y compris le CAST final () et n'effectue aucune concaténation de chaîne, ce qui est un plus automatique. De plus, il n'y a pas de transtypages énormes ici. Si vous pouvez imaginer que les horodatages peuvent être représentés, la conversion de dates en nombres et de retour en dates est un processus assez simple.

(2)

CREATE FUNCTION trunc_date(@date DATETIME)
RETURNS DATETIME
AS
BEGIN
      SELECT CONVERT(varchar, @date,112)
END

Si vous êtes préoccupé par l'implémentation par microsoft des datetimes (2) ou (3), cela peut être correct.

(3)

CREATE FUNCTION trunc_date(@date DATETIME)
RETURNS DATETIME
AS
BEGIN
SELECT CAST((STR( YEAR( @date ) ) + '/' +STR( MONTH( @date ) ) + '/' +STR( DAY(@date ) )
) AS DATETIME
END

Troisièmement, la méthode la plus verbeuse. Cela nécessite de diviser la date en ses parties d'année, de mois et de jour, de les assembler au format "aaaa / mm / jj", puis de les restituer à une date. Cette méthode implique 7 appels de méthode dont le CAST () final, sans parler de la concaténation de chaînes.

AlejandroR
la source
1
CONVERT(DATE, <yourdatetime>) or CONVERT(DATE, GetDate()) or CONVERT(DATE, CURRENT_TIMESTAMP)
doyen
la source
0

Pour ceux d'entre vous qui sont venus ici à la recherche d'un moyen de tronquer un champ DATETIME à quelque chose de moins qu'une journée entière, par exemple chaque minute, vous pouvez utiliser ceci:

SELECT CAST(FLOOR(CAST(GETDATE() AS FLOAT)) + (FLOOR((CAST(GETDATE() AS FLOAT) - FLOOR(CAST(GETDATE() AS FLOAT))) * 1440.0) + (3.0/86400000.0)) / 1440.0 AS DATETIME)

donc si c'était aujourd'hui, 2010-11-26 14:54:43.123cela reviendrait2010-11-26 14:54:00.000 .

Pour modifier l'intervalle auquel il se traduit, remplacez 1440.0 par le nombre d'intervalles dans une journée, par exemple:

24hrs          =   24.0  (for every hour)
24hrs / 0.5hrs =   48.0  (for every half hour)
24hrs / (1/60) = 1440.0  (for every minute)

(Mettez toujours un .0à la fin pour lancer implicitement un flottant.)


Pour ceux d'entre vous qui se demandent à quoi (3.0/86400000)sert mon calcul, SQL Server 2005 ne semble pas être converti de FLOATà DATETIMEprécisément, donc cela ajoute 3 millisecondes avant de le poser.

BG100
la source
1
Soyez prudent avec les erreurs d'arrondi dues aux limites de précision en virgule flottante, bien que ... cela ne fonctionne pas non plus avec le datetime2type de données.
Lucero
Pour l'heure, SELECT DATEADD (heure, DATEDIFF (heure, 0, GETDATE ()), 0) fonctionne également. Minute aussi, mais Second entraînera un débordement.
Kelly Cline
La conversion pour flotter et revenir à datetime ne fonctionne pas correctement .
ErikE
0

Cette requête devrait vous donner un résultat équivalent à trunc(sysdate)dans Oracle.

SELECT  * 
FROM    your_table
WHERE   CONVERT(varchar(12), your_column_name, 101)
      = CONVERT(varchar(12), GETDATE(), 101)

J'espère que cela t'aides!

Sandeep Gaadhe
la source
0

Vous pouvez également extraire la date using Substringde la variable datetime et la restitution à datetime ignorera la partie temps.

declare @SomeDate datetime = '2009-05-28 16:30:22'
SELECT cast(substring(convert(varchar(12),@SomeDate,111),0,12) as Datetime) 

De plus, vous pouvez accéder à des parties de la variable datetime et les fusionner avec une construction à date tronquée, quelque chose comme ceci:

SELECT cast(DATENAME(year, @Somedate) + '-' + 
       Convert(varchar(2),DATEPART(month, @Somedate)) + '-' +
       DATENAME(day, @Somedate) 
       as datetime)
NeverHopeless
la source
0

Oracle:

TRUNC(SYSDATE, 'MONTH')

Serveur SQL:

DATEADD(DAY, - DATEPART(DAY, DateField) + 1, DateField)

Peut également être utilisé pour tronquer les minutes ou les heures à partir d'une date.

Markus
la source
0

vous pouvez simplement le faire (SQL 2008):

declare @SomeDate date = getdate ()

select @SomeDate

2009-05-28

Hagai Danenberg-Lerner
la source