SQL Server 2008 och senare
I SQL Server 2008 och senare är naturligtvis det snabbaste sättet Convert(date, @date)
. Detta kan skickas tillbaka till en datetime
eller datetime2
vid behov.
Vad är egentligen bäst i SQL Server 2005 och äldre?
Jag har sett inkonsekventa påståenden om vad som är snabbast för att trunkera tiden från ett datum i SQL Server, och vissa personer sa till och med att de gjorde tester, men min erfarenhet har varit annorlunda. Så låt oss göra lite mer stränga tester och låta alla ha skriptet så att om jag gör några misstag kan folk rätta mig.
Flytande omvandlingar är inte korrekta
Först skulle jag hålla mig borta från att konvertera datetime
att float
, eftersom den inte konverterar korrekt. Du kanske kommer undan med att göra tidsborttagningen korrekt, men jag tror att det är en dålig idé att använda det eftersom det implicit kommunicerar till utvecklare att detta är en säker operation och det är det inte . Ta en titt:
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
Detta är inte något vi borde lära människor i vår kod eller i våra exempel online.
Dessutom är det inte ens det snabbaste sättet!
Bevis – prestandatestning
Om du vill utföra några tester själv för att se hur de olika metoderna verkligen fungerar, behöver du det här installationsskriptet för att köra testen längre ner:
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
Observera att detta skapar en tabell på 427,57 MB i din databas och att det tar ungefär 15-30 minuter att köra. Om din databas är liten och inställd på 10 % tillväxt kommer det att ta längre tid än om du gör en tillräckligt stor först.
Nu till själva prestationstestningsskriptet. Observera att det är ändamålsenligt att inte returnera rader tillbaka till klienten eftersom detta är galet dyrt på 26 miljoner rader och skulle dölja prestandaskillnaderna mellan metoderna.
Prestanda resultat
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;
Några tråkiga analyser
Några anteckningar om detta. Först och främst, om du bara utför en GROUP BY eller en jämförelse, behöver du inte konvertera tillbaka till datetime
. Så du kan spara lite CPU genom att undvika det, om du inte behöver det slutliga värdet för visningsändamål. Du kan till och med GROUP BY det okonverterade värdet och sätta konverteringen endast i SELECT-satsen:
select Convert(datetime, DateDiff(dd, 0, Tm))
from (select '2010-09-12 00:00:00.003') X (Tm)
group by DateDiff(dd, 0, Tm)
Se också hur de numeriska omvandlingarna bara tar något längre tid att konvertera tillbaka till datetime
, men varchar
konvertering nästan fördubblas? Detta avslöjar den del av CPU:n som är ägnad åt datumberäkning i frågorna. Det finns delar av CPU-användningen som inte involverar datumberäkning, och detta verkar vara något nära 19875 ms i ovanstående frågor. Sedan tar konverteringen ytterligare ett belopp, så om det finns två omvandlingar används det beloppet ungefär två gånger.
Fler undersökningar visar att jämfört med Convert(, 112)
, Convert(, 101)
frågan har en viss extra CPU-kostnad (eftersom den använder en längre varchar
?), eftersom den andra konverteringen tillbaka till date
kostar inte lika mycket som den första konverteringen till varchar
, men med Convert(, 112)
det är närmare samma 20 000 ms CPU-baskostnad.
Här är dessa beräkningar på CPU-tiden som jag använde för ovanstående analys:
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
-
runda är CPU-tiden för en tur och retur tillbaka till
datetime
. -
singel är CPU-tid för en enda konvertering till den alternativa datatypen (den som har bieffekten att tidsdelen tas bort).
-
bas är beräkningen av att subtrahera från
single
skillnaden mellan de två anropen:single - (round - single)
. Det är en bollplank som antar konverteringen till och från den datatypen ochdatetime
är ungefär densamma i båda riktningarna. Det verkar som om detta antagande inte är perfekt men är nära eftersom värdena alla är nära 20 000 ms med bara ett undantag.
En mer intressant sak är att baskostnaden är nästan lika med den enda Convert(date)
metod (som måste vara nästan 0 kostnad, eftersom servern internt kan extrahera heltalsdagsdelen direkt ur de första fyra byten av datetime
datatyp).
Slutsats
Så vad det ser ut är att enkelriktad varchar
konverteringsmetoden tar cirka 1,8 μs och enkelriktad DateDiff
metoden tar cirka 0,18 μs. Jag baserar detta på den mest konservativa "bas-CPU"-tiden i mina tester på totalt 18458 ms för 25 920 000 rader, så 23 218 ms / 25920000 =0,18 μs. Den uppenbara 10x förbättringen verkar vara mycket, men den är ärligt talat ganska liten tills du har att göra med hundratusentals rader (617 000 rader =1 sekunds besparing).
Även med tanke på denna lilla absoluta förbättring, enligt min mening, DateAdd
metoden vinner eftersom det är den bästa kombinationen av prestanda och tydlighet. Svaret som kräver ett "magiskt tal" på 0.50000004
kommer att bita någon någon dag (fem nollor eller sex???), plus att det är svårare att förstå.
Ytterligare anteckningar
När jag får lite tid ska jag ändra 0.50000004
till '12:00:00.003'
och se hur det gör. Den konverteras till samma datetime
värde och jag tycker det är mycket lättare att komma ihåg.
För de intresserade kördes ovanstående tester på en server där @@Version returnerar följande:
Microsoft SQL Server 2008 (RTM) - 10.0.1600.22 (Intel X86) 9 jul 2008 14:43:34 Copyright (c) 1988-2008 Microsoft Corporation Standard Edition på Windows NT 5.2 (Build 3790:Service Pack 2)