Domanda

Ecco quello che uso io:

SELECT CAST(FLOOR(CAST(getdate() as FLOAT)) as DATETIME)

Sto pensando che non ci può essere una migliore e più elegante.

Requisiti:

  • Deve essere il più veloce possibile (meno di casting, la migliore).
  • Il risultato finale deve essere un datetime tipo, non una stringa.
È stato utile?

Soluzione

SQL Server 2008 e fino

In SQL Server 2008 e, naturalmente, il modo più veloce è Convert(date, @date).Questo può essere gettato di nuovo ad un datetime o datetime2 se necessario.

Che cosa È Davvero Meglio In SQL Server 2005 e versioni Precedenti?

Ho visto inconsistenti affermazioni su ciò che è più veloce per il troncamento del tempo da una data in SQL Server, e alcune persone anche detto che ha fatto il test, ma la mia esperienza è stata diversa.Quindi cerchiamo di fare un po ' più severi test e permettere a tutti di avere lo script quindi se faccio errori gente possibile correggere me.

Float Conversioni Non Sono Precisi

Primo, vorrei rimanere lontano dalla conversione datetime per float, perché non convertire correttamente.Si può ottenere via con fare il tempo di rimozione cosa con precisione, ma penso che sia una cattiva idea di usarlo, perché implicitamente comunica agli sviluppatori che questo è un funzionamento sicuro e non è.Date un'occhiata:

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

Questo non è qualcosa che dovremmo insegnare alle persone nel nostro codice o nei nostri esempi online.

Inoltre, non è ancora il modo più veloce!

La Prova – La Prova Di Prestazioni

Se si desidera eseguire alcuni test voi stessi per vedere come i diversi metodi realmente stack, quindi avrete bisogno di questo script di installazione per eseguire il test più in basso:

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

Si prega di notare che questo crea un 427.57 MB tabella nel database e prendere qualcosa di simile a 15-30 minuti per l'esecuzione.Se il database è piccolo e impostato in crescita del 10% che ci vorrà più tempo se è abbastanza grande prima.

Ora per le prestazioni effettive test script.Si prega di notare che è risoluta a non restituire righe al client come questo è pazzo costoso 26 milioni di righe e nascondere le differenze di prestazioni tra i due metodi.

I Risultati Delle Prestazioni

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;

Alcuni Vagante Analisi

Alcune note su questo.Prima di tutto, se solo l'esecuzione di un GRUPPO o di un confronto, non c'è bisogno di convertire datetime.Così si può risparmiare un po di CPU, evitando che, a meno che non è necessario il valore finale per scopi di visualizzazione.Si può anche GRUPPO DA non convertito valore e mettere solo la conversione nella clausola 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)

Inoltre, vedere come le conversioni numeriche solo prendere un po ' più di tempo per convertire datetime, ma il varchar conversione quasi raddoppia?Questo rivela la parte della CPU che è dedicato al calcolo della data della query.Ci sono parti dell'utilizzo della CPU che non comportano la data di calcolo, e questo sembra essere qualcosa di simile a 19875 ms richieste di cui sopra.Quindi la conversione richiede qualche ulteriore importo, in modo che se ci sono due conversioni, tale importo è utilizzato con un aumento di circa due volte.

Più esame rivela che, rispetto a Convert(, 112), il Convert(, 101) query aggiuntive CPU spese (in quanto utilizza una più varchar?), perché la seconda conversione in date non costa tanto quanto la conversione iniziale di varchar, ma con Convert(, 112) è più vicino alla stessa 20000 ms CPU costo di base.

Qui ci sono quei calcoli sul tempo di CPU che ho usato per l'analisi di cui sopra:

     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
  • giro è il tempo di CPU per un andata e ritorno indietro per datetime.

  • singolo è tempo di CPU per una singola conversione di dati alternativo tipo (quello che ha l'effetto collaterale di rimozione della porzione di tempo).

  • base è il calcolo di sottrazione da single la differenza tra le due invocazioni: single - (round - single).Si tratta di un ballpark figura che assume la conversione in e da che tipo di dati e datetime è circa lo stesso in entrambe le direzioni.Sembra che questo presupposto non è perfetto, ma è vicino, perché i valori sono tutti vicino a 20000 ms, con una sola eccezione.

Una cosa interessante è che il costo base è quasi uguale al singolo Convert(date) metodo (che deve essere quasi a costo 0, in quanto il server è in grado internamente estrarre l'intero giorno parte a destra, fuori i primi quattro byte del datetime tipo di dati).

Conclusione

Quindi, quello che sembra è che la sola direzione varchar metodo di conversione dura circa 1.8 µs e la singola direzione DateDiff il metodo richiede circa 0.18 µs.Mi sto basando su questo, il più conservativo "CPU base" tempo nel mio test di 18458 ms totale per 25,920,000 righe, in modo 23218 ms / 25920000 = 0.18 µs.L'apparente 10x miglioramento sembra come un sacco, ma è francamente piuttosto piccolo fino a quando avete a che fare con centinaia di migliaia di righe (617k righe = 1 secondo di risparmio).

Anche questo piccolo miglioramento assoluto, a mio parere, il DateAdd metodo vince perchè è la migliore combinazione di prestazioni e di chiarezza.La risposta che richiede un "numero magico" di 0.50000004 sta per mordere qualcuno un giorno (cinque o sei zeri???), in più è più difficile da capire.

Note Aggiuntive

Quando ho un po di tempo ho intenzione di cambiare 0.50000004 per '12:00:00.003' e vedere come si fa.Si è convertito alla stessa datetime valore e trovo molto più facile da ricordare.

Per chi fosse interessato, le prove di cui sopra sono stati eseguiti su un server dove @@Versione restituisce il seguente:

Microsoft SQL Server 2008 (RTM) - 10.0.1600.22 (Intel X86) 9 Lug 2008 14:43:34 Copyright (c) 1988-2008 Microsoft Corporation Standard Edition su Windows NT 5.2 (Build 3790:Service Pack 2)

Altri suggerimenti

SQL Server 2008 è un nuovo data tipo di dati e questo semplifica questo problema:

SELECT CAST(CAST(GETDATE() AS date) AS datetime)

Itzik Ben-Gan in DATETIME Calcoli, Parte 1 (SQL Server Magazine, febbraio 2007) mostra tre metodi per eseguire la conversione (più lento al più veloce;la differenza tra il secondo e il terzo metodo è piccolo):

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)

La tua tecnica (casting per galleggiante) è suggerito da un lettore nel numero di aprile della rivista.Secondo lui, ha prestazioni paragonabili a quelle di seconda tecnica di cui sopra.

Il CAST-FLOOR-CAST sembra già di essere il migliore, almeno su MS SQL Server 2005.

Altre soluzioni che ho visto sono una stringa di conversione, come Select Convert(varchar(11), getdate(),101) in loro, che è più lento di un fattore 10.

Si prega di provare:

SELECT CONVERT(VARCHAR(10),[YOUR COLUMN NAME],105) [YOURTABLENAME]

SQL2005:Mi raccomando cast invece di dateadd.Per esempio,

select cast(DATEDIFF(DAY, 0, datetimefield) as datetime)

una media di circa il 10% più veloce sul mio set di dati, di

select DATEADD(DAY, DATEDIFF(DAY, 0, datetimefield), 0)

(e la fusione in smalldatetime è stato ancora più veloce)

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top