SQL Server 2008 a vyšší
V SQL Server 2008 a novějších je samozřejmě nejrychlejší způsob Convert(date, @date)
. Toto lze přenést zpět na datetime
nebo datetime2
v případě potřeby.
Co je skutečně nejlepší na SQL Server 2005 a starších?
Viděl jsem rozporuplná tvrzení o tom, co je nejrychlejší pro zkrácení času z data na serveru SQL Server, a někteří lidé dokonce řekli, že testovali, ale moje zkušenost byla jiná. Udělejme tedy přísnější testování a nechme scénář mít každý, takže pokud udělám nějaké chyby, lidé mě mohou opravit.
Plovoucí konverze nejsou přesné
Nejprve bych se držel dál od převodu datetime
float
, protože se nepřevádí správně. Možná vám projde, když uděláte věc s časovým odstraněním přesně, ale myslím si, že je špatný nápad to používat, protože to implicitně sděluje vývojářům, že se jedná o bezpečnou operaci a to není . Podívejte se:
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
To není něco, co bychom měli učit lidi v našem kódu nebo v našich příkladech online.
Navíc to není ani nejrychlejší způsob!
Důkaz – testování výkonu
Pokud chcete sami provést nějaké testy, abyste viděli, jak se různé metody skutečně skládají, pak budete potřebovat tento instalační skript, abyste testy spustili dále:
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
Upozorňujeme, že tím se ve vaší databázi vytvoří tabulka o velikosti 427,57 MB a její spuštění bude trvat přibližně 15–30 minut. Je-li vaše databáze malá a nastavená na 10% růst, bude to trvat déle, než kdybyste nejprve vytvořili dostatečně velkou velikost.
Nyní k samotnému skriptu testování výkonu. Vezměte prosím na vědomí, že je účelné nevracet řádky zpět klientovi, protože to je u 26 milionů řádků šíleně drahé a skrylo by to rozdíly ve výkonu mezi metodami.
Výsledky výkonu
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ěkterá zbytečná analýza
Pár poznámek k tomu. Za prvé, pokud pouze provádíte GROUP BY nebo porovnání, není třeba převádět zpět na datetime
. Takže můžete ušetřit nějaké CPU tím, že se tomu vyhnete, pokud nepotřebujete konečnou hodnotu pro účely zobrazení. Můžete dokonce GROUP BY nepřevedenou hodnotu a převod umístit pouze do klauzule 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)
Podívejte se také, jak numerickým převodům trvá převod zpět na datetime
o něco déle , ale varchar
konverze téměř dvojnásobná? To odhaluje část CPU, která je věnována výpočtu data v dotazech. Existují části využití procesoru, které nezahrnují výpočet data, a zdá se, že ve výše uvedených dotazech je to něco blízkého 19875 ms. Pak konverze vyžaduje nějakou další částku, takže pokud dojde ke dvěma konverzím, tato částka se vyčerpá přibližně dvakrát.
Další zkoumání ukazuje, že ve srovnání s Convert(, 112)
, Convert(, 101)
dotaz má nějaké další náklady na CPU (protože používá delší varchar
?), protože druhý převod zpět na date
nestojí tolik jako počáteční konverze na varchar
, ale pomocí Convert(, 112)
je blíže stejným základním nákladům na CPU 20 000 ms.
Zde jsou výpočty času CPU, které jsem použil pro výše uvedenou analýzu:
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
-
kulaté je čas CPU pro zpáteční cestu zpět do
datetime
. -
single je čas CPU pro jeden převod na alternativní datový typ (ten, který má vedlejší účinek odstranění časové části).
-
základ je výpočet odečítání od
single
rozdíl mezi dvěma vyvoláními:single - (round - single)
. Je to orientační údaj, který předpokládá převod do az daného datového typu adatetime
je přibližně stejný v obou směrech. Zdá se, že tento předpoklad není dokonalý, ale je blízko, protože všechny hodnoty jsou blízko 20 000 ms s jedinou výjimkou.
Ještě jedna zajímavá věc je, že základní cena se téměř rovná jedné Convert(date)
metoda (což musí být téměř nulové, protože server dokáže interně extrahovat celočíselnou část dne přímo z prvních čtyř bajtů datetime
datový typ).
Závěr
Takže to vypadá, že jednosměrný varchar
metoda konverze trvá asi 1,8 μs a jednosměrná metoda DateDiff
metoda trvá asi 0,18 μs. Vycházím z nejkonzervativnějšího času „základního procesoru“ při mém testování celkem 18 458 ms pro 25 920 000 řádků, takže 2 3218 ms / 25920 000 =0,18 μs. Zdánlivé 10násobné zlepšení se zdá být hodně, ale upřímně řečeno je to docela malé, dokud nebudete mít co do činění se stovkami tisíc řádků (617 tisíc řádků =úspora 1 sekundy).
I vzhledem k tomuto malému absolutnímu zlepšení, podle mého názoru, DateAdd
metoda vítězí, protože je nejlepší kombinací výkonu a srozumitelnosti. Odpověď, která vyžaduje "magické číslo" 0.50000004
jednoho dne někoho kousne (pět nul nebo šest???), navíc je to těžší na pochopení.
Další poznámky
Až budu mít trochu času, změním 0.50000004
na '12:00:00.003'
a uvidíte, jak to jde. Převede se na stejné datetime
hodnotu a je pro mě mnohem snazší si ji zapamatovat.
Pro zájemce byly výše uvedené testy spuštěny na serveru, kde @@Version vrací následující:
Microsoft SQL Server 2008 (RTM) – 10.0.1600.22 (Intel X86) 9. července 2008 14:43:34 Copyright (c) 1988-2008 Microsoft Corporation Standard Edition v systému Windows NT 5.2 (sestavení 3790:Service Pack 2)