SQL Server 2008 en hoger
In SQL Server 2008 en hoger is de snelste manier natuurlijk Convert(date, @date)
. Dit kan worden teruggedraaid naar een datetime
of datetime2
indien nodig.
Wat is echt het beste in SQL Server 2005 en ouder?
Ik heb inconsistente beweringen gezien over wat het snelst is om de tijd vanaf een datum in SQL Server af te kappen, en sommige mensen zeiden zelfs dat ze getest hebben, maar mijn ervaring is anders. Dus laten we wat strengere tests doen en iedereen het script geven, zodat als ik fouten maak, mensen me kunnen corrigeren.
Float-conversies zijn niet nauwkeurig
Ten eerste zou ik wegblijven van het converteren van datetime
naar float
, omdat het niet correct wordt geconverteerd. Je komt misschien weg met het nauwkeurig uitvoeren van het time-removal-ding, maar ik denk dat het een slecht idee is om het te gebruiken omdat het impliciet aan ontwikkelaars communiceert dat dit een veilige operatie is en het is niet . Kijk eens:
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
Dit is niet iets dat we mensen zouden moeten leren in onze code of in onze online voorbeelden.
Het is ook niet eens de snelste manier!
Bewijs – Prestatietests
Als je zelf wat tests wilt uitvoeren om te zien hoe de verschillende methoden het echt doen, dan heb je dit setup-script nodig om de tests verderop uit te voeren:
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
Houd er rekening mee dat hierdoor een tabel van 427,57 MB in uw database wordt gemaakt en het uitvoeren ervan ongeveer 15-30 minuten duurt. Als uw database klein is en is ingesteld op 10% groei, duurt het langer dan wanneer u eerst groot genoeg wordt.
Nu voor het eigenlijke prestatietestscript. Houd er rekening mee dat het doelbewust is om geen rijen terug te sturen naar de client, omdat dit waanzinnig duur is voor 26 miljoen rijen en de prestatieverschillen tussen de methoden zou verbergen.
Prestatieresultaten
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;
Enkele rambling-analyse
Enkele opmerkingen hierover. Allereerst, als u alleen een GROUP BY of een vergelijking uitvoert, hoeft u niet terug te converteren naar datetime
. U kunt dus wat CPU besparen door dat te vermijden, tenzij u de uiteindelijke waarde nodig heeft voor weergavedoeleinden. U kunt zelfs GROUPEREN OP de niet-geconverteerde waarde en de conversie alleen in de SELECT-clausule plaatsen:
select Convert(datetime, DateDiff(dd, 0, Tm))
from (select '2010-09-12 00:00:00.003') X (Tm)
group by DateDiff(dd, 0, Tm)
Zie ook hoe de numerieke conversies slechts iets meer tijd kosten om terug te converteren naar datetime
, maar de varchar
conversie bijna verdubbeld? Dit onthult het deel van de CPU dat is gewijd aan datumberekening in de query's. Er zijn delen van het CPU-gebruik waarbij geen datumberekening is betrokken, en dit lijkt iets in de buurt van 19875 ms te zijn in de bovenstaande zoekopdrachten. Dan kost de conversie een extra bedrag, dus als er twee conversies zijn, wordt dat bedrag ongeveer twee keer opgebruikt.
Nader onderzoek laat zien dat in vergelijking met Convert(, 112)
, de Convert(, 101)
query heeft wat extra CPU-kosten (omdat het een langere varchar
gebruikt) ?), omdat de tweede conversie terug naar date
kost niet zoveel als de eerste conversie naar varchar
, maar met Convert(, 112)
het ligt dichter bij dezelfde 20000 ms CPU-basiskosten.
Hier zijn de berekeningen van de CPU-tijd die ik heb gebruikt voor de bovenstaande analyse:
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 is de CPU-tijd voor een retourtje terug naar
datetime
. -
enkel is CPU-tijd voor een enkele conversie naar het alternatieve gegevenstype (degene die als neveneffect heeft dat het tijdgedeelte wordt verwijderd).
-
basis is de berekening van het aftrekken van
single
het verschil tussen de twee aanroepingen:single - (round - single)
. Het is een margecijfer dat de conversie van en naar dat gegevenstype endatetime
. aanneemt is in beide richtingen ongeveer hetzelfde. Het lijkt erop dat deze aanname niet perfect is, maar in de buurt komt, omdat de waarden allemaal dicht bij 20000 ms liggen, met slechts één uitzondering.
Nog een interessant ding is dat de basiskosten bijna gelijk zijn aan de enkele Convert(date)
methode (die bijna 0 kosten moet zijn, omdat de server intern het gehele daggedeelte kan extraheren uit de eerste vier bytes van de datetime
gegevenstype).
Conclusie
Dus hoe het eruit ziet is dat de eenrichtings varchar
conversiemethode duurt ongeveer 1,8 μs en de single-directionele DateDiff
methode duurt ongeveer 0,18 μs. Ik baseer dit op de meest conservatieve "basis CPU"-tijd in mijn testen van 18458 ms totaal voor 25.920.000 rijen, dus 23218 ms / 25920000 =0,18 μs. De schijnbare 10x verbetering lijkt veel, maar het is eerlijk gezegd vrij klein totdat je te maken hebt met honderdduizenden rijen (617k rijen =1 seconde besparing).
Zelfs gezien deze kleine absolute verbetering, is naar mijn mening de DateAdd
methode wint omdat het de beste combinatie is van prestatie en duidelijkheid. Het antwoord dat een "magisch getal" van 0.50000004
. vereist gaat op een dag iemand bijten (vijf nullen of zes???), en het is moeilijker te begrijpen.
Aanvullende opmerkingen
Als ik wat tijd heb ga ik 0.50000004
veranderen naar '12:00:00.003'
en kijk hoe het gaat. Het wordt geconverteerd naar dezelfde datetime
waarde en ik vind het veel gemakkelijker te onthouden.
Voor degenen die geïnteresseerd zijn, de bovenstaande tests zijn uitgevoerd op een server waar @@Version het volgende retourneert:
Microsoft SQL Server 2008 (RTM) - 10.0.1600.22 (Intel X86) 9 juli 2008 14:43:34 Copyright (c) 1988-2008 Microsoft Corporation Standard Edition op Windows NT 5.2 (Build 3790:Service Pack 2)