Come posso troncare un datetime in SQL Server?


281

Qual è il modo migliore per troncare un valore datetime (come rimuovere ore minuti e secondi) in SQL Server 2008?

Per esempio:

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

-----------------------
2009-05-28 00:00:00.000

Risposte:


495

Questo continua a raccogliere frequentemente ulteriori voti, anche diversi anni dopo, e quindi ho bisogno di aggiornarlo per le versioni moderne di SQL Server. Per SQL Server 2008 e versioni successive, è semplice:

cast(getDate() As Date)

Si noti che gli ultimi tre paragrafi nella parte inferiore si applicano ancora e spesso è necessario fare un passo indietro e trovare un modo per evitare il cast in primo luogo.

Ma ci sono anche altri modi per farlo. Ecco i più comuni.

Il modo corretto (novità da SQL Server 2008):

cast(getdate() As Date)

Il modo corretto (vecchio):

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

Ora è più vecchio, ma vale comunque la pena sapere perché può anche adattarsi facilmente ad altri punti temporali, come il primo momento del mese, minuto, ora o anno.

Questo modo corretto utilizza funzioni documentate che fanno parte dello standard ansi e sono garantite per funzionare, ma può essere un po 'più lento. Funziona trovando quanti giorni ci sono dal giorno 0 al giorno corrente e aggiungendo quanti giorni al giorno 0. Funzionerà indipendentemente dal tempo di archiviazione dei dati e dalle impostazioni locali.

Il modo veloce:

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

Ciò funziona perché le colonne datetime sono archiviate come valori binari a 8 byte. Lanciali per fluttuare, pavimentali per rimuovere la frazione e la parte temporale dei valori scompare quando li rilasci in datetime. Tutto sta cambiando un po 'senza una logica complicata ed è molto veloce.

Tieni presente che questo si basa su un dettaglio di implementazione che Microsoft è libera di modificare in qualsiasi momento, anche in un aggiornamento automatico del servizio. Inoltre non è molto portatile. In pratica, è molto improbabile che questa implementazione cambierà presto, ma è comunque importante essere consapevoli del pericolo se si sceglie di usarlo. E ora che abbiamo la possibilità di scegliere come data, raramente è necessario.

La strada sbagliata:

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

Il modo sbagliato funziona convertendosi in una stringa, troncando la stringa e convertendo nuovamente in un datetime. È sbagliato , per due motivi: 1) potrebbe non funzionare in tutti i locali e 2) è il modo più lento possibile per farlo ... e non solo un po '; è come un ordine di grandezza o due più lento delle altre opzioni.


Aggiornamento Questo ha ottenuto recentemente alcuni voti, e quindi voglio aggiungere ad esso che da quando ho pubblicato questo ho visto alcune prove abbastanza solide che Sql Server ottimizzerà la differenza di prestazioni tra il modo "corretto" e il modo "veloce" , il che significa che ora dovresti favorire il primo.

In entrambi i casi, vuoi scrivere le tue domande per evitare la necessità di farlo in primo luogo . È molto raro che tu debba fare questo lavoro sul database.

Nella maggior parte dei casi, il database è già il collo di bottiglia. In genere è il server a cui è più costoso aggiungere hardware per migliorare le prestazioni e quello più difficile per ottenere quelle aggiunte giuste (ad esempio, è necessario bilanciare i dischi con la memoria). È anche il più difficile da ridimensionare verso l'esterno, sia tecnicamente che dal punto di vista aziendale; è tecnicamente molto più semplice aggiungere un server Web o applicativo rispetto a un server database e anche se fosse falso non si pagano $ 20.000 + per licenza server per IIS o apache.

Il punto che sto cercando di sottolineare è che quando possibile dovresti fare questo lavoro a livello di applicazione. L' unica volta in cui dovresti mai ritrovarti a troncare un datetime su Sql Server è quando devi raggruppare di giorno in giorno, e anche allora dovresti probabilmente avere una colonna aggiuntiva impostata come colonna calcolata, mantenuta al momento dell'inserimento / aggiornamento o mantenuta nella logica dell'applicazione. Rimuovi dal database questo lavoro che rompe l'indice e fa molto lavoro.


6
il "modo veloce" è ancora il modo più veloce per sql 2008 secondo un benchmark che ho appena eseguito
Sam Saffron,

3
FYI: stackoverflow.com/q/1177449/27535 e stackoverflow.com/q/133081/27535 Il dateadd / DateDiff "vince ...". Per una singola variabile, a chi importa ovviamente, e si spera che tu abbia calcolato colonne o simili su un milione di righe :-)
gbn

9
Questo modo "corretto" funziona solo accidentalmente. Il modo in cui è scritto è come se la sintassi di DateAdd fosse (intervallo, data, incremento), ma non lo è. È (intervallo, incremento, data). Mi sono imbattuto in questo quando ho provato a troncare una data al primo del mese: SELECT DATEADD (m, 0, DATEDIFF (m, 0, GETDATE ())) non funziona, ma SELECT DATEADD (m, DATEDIFF (m, 0, GETDATE ()), 0) fa. Almeno, questo è quello che vedo nel 2008R2.
Kelly Cline,

1
@Kelly nel 2008R2, perché non solo cast(getdate() as date)?
Joel Coehoorn,

2
Funzionano tutti su una colonna datetime. getdate()ecco uno stand-in per qualsiasi fonte datetime potresti avere.
Joel Coehoorn,

44

Solo per SQL Server 2008

CAST(@SomeDateTime AS Date) 

Quindi esegui il cast di nuovo al datetime se lo desideri

CAST(CAST(@SomeDateTime AS Date) As datetime)

Un buon punto: sono ancora nel 2005 e quindi per il 2008 questo è probabilmente il nuovo modo "corretto" e potrebbe persino eguagliare le prestazioni del modo "veloce".
Joel Coehoorn,

1
Le prestazioni di questo nuovo modo sono persino più veloci del modo "veloce".
ErikE,

21

Solo per una risposta più completa, ecco un modo di lavorare per troncare una delle parti della data in giù e includendo i minuti (sostituire GETDATE()con la data per troncare).

Questo è diverso dalla risposta accettata in quanto è possibile utilizzare non solo dd(giorni), ma anche una delle parti della data (vedi qui ):

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

Si noti che nell'espressione sopra, 0è una data costante all'inizio di un anno (1900-01-01). Se è necessario troncare in parti più piccole, ad esempio in secondi o millisecondi, è necessario prendere una data costante più vicina alla data da troncare per evitare un overflow.


1
Questo è stato mostruosamente utile. Ho cercato dappertutto un modo per troncare la data e l'ora in un posto inferiore all'intera giornata.
Michael - Dov'è Clay Shirky il

1
@Michael, grazie per il feedback, buono a sapersi che ti ha aiutato!
Lucero,

1
+1 questo dovrebbe avere più voti, è un'ottima risposta che si espande sulla risposta selezionata.
jtate,

1
Solo così Internet lo sa, non devi essere limitato a periodi di datepart completi. Ecco un esempio per intervalli di 15 minuti, usando la divisione intera:dateadd(minute, datediff(minute, 0, GETDATE()) / 15 * 15, 0)
Michael - Where's Clay Shirky,

7

Lo snippet che ho trovato sul web quando dovevo farlo era:

 dateadd(dd,0, datediff(dd,0, YOURDATE))
 e.g.
 dateadd(dd,0, datediff(dd,0, getDate()))

Sono il 2005, ma pensavo che il 2008 avesse qualche nuova funzione per questo ??
KM.

2
! Neat Avrei fatto ricorso a dividere le date date e usare la gestione delle stringhe per rimetterle insieme. Potrebbe non essere pertinente, ma SQL2008 ha un tipo di dati solo data senza un elemento time.
Frans,

1
E nota che hai gli operandi DateAdd confusi, lo è DateAdd(dd, DateDiff(...), 0). Questo può morderti se non stai attento.
ErikE

1

In SQl 2005 la tua funzione trunc_date potrebbe essere scritta in questo modo.

(1)

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

Il primo metodo è molto più pulito. Utilizza solo 3 chiamate di metodo incluso CAST () finale e non esegue alcuna concatenazione di stringhe, che è un vantaggio automatico. Inoltre, non ci sono cast di tipo enorme qui. Se puoi immaginare che i timbri data / ora possano essere rappresentati, la conversione da date a numeri e di nuovo a date è un processo abbastanza semplice.

(2)

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

Se sei preoccupato per l'implementazione di Microsoft dei tempi dei dati (2) o (3), potrebbe andare bene.

(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

Terzo, il metodo più dettagliato. Ciò richiede di suddividere la data nelle sue parti di anno, mese e giorno, metterle insieme nel formato "aaaa / mm / gg", quindi riportare quella data a una data. Questo metodo prevede 7 chiamate di metodo incluso l'ultimo CAST (), per non parlare della concatenazione di stringhe.


1
CONVERT(DATE, <yourdatetime>) or CONVERT(DATE, GetDate()) or CONVERT(DATE, CURRENT_TIMESTAMP)


0

Per quelli di voi che sono venuti qui alla ricerca di un modo per troncare un campo DATETIME in qualcosa di meno di un giorno intero, ad esempio ogni minuto, potete usare questo:

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)

quindi se oggi fosse 2010-11-26 14:54:43.123allora questo sarebbe tornato 2010-11-26 14:54:00.000.

Per modificare l'intervallo in cui si basa, sostituire 1440.0 con il numero di intervalli in un giorno, ad esempio:

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

(Metti sempre un .0alla fine per lanciare implicitamente un float.)


Per quelli di voi che si chiedono a cosa servono i (3.0/86400000)miei calcoli, SQL Server 2005 non sembra eseguire il cast FLOATcon DATETIMEprecisione, quindi questo aggiunge 3 millisecondi prima di pavimentarlo.


1
Fai attenzione agli errori di arrotondamento dovuti ai limiti di precisione in virgola mobile, tuttavia ... Inoltre, questo non funziona con il datetime2tipo di dati.
Lucero,

Per l'ora, SELEZIONA DATEADD (ora, DATEDIFF (ora, 0, GETDATE ()), 0) funziona anche. Anche minuti, ma Second comporterà un overflow.
Kelly Cline,

Il cast su float e back to datetime non funziona correttamente .
ErikE

0

Questa query dovrebbe fornire un risultato equivalente a trunc(sysdate)Oracle.

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

Spero che questo ti aiuti!


0

Puoi anche estrarre la data using Substringdalla variabile datetime e tornare indietro a datetime ignorerà la parte temporale.

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

Inoltre, puoi accedere a parti della variabile datetime e unirle in una data troncata del costrutto, in questo modo:

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

0

Oracolo:

TRUNC(SYSDATE, 'MONTH')

Server SQL:

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

Potrebbe essere usato allo stesso modo per troncare minuti o ore da una data.



Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.