SQL Server 2008 i nowsze
W SQL Server 2008 i nowszych oczywiście najszybszym sposobem jest Convert(date, @date)
. Można to przywrócić do datetime
lub datetime2
jeśli to konieczne.
Co jest naprawdę najlepsze w SQL Server 2005 i starszych?
Widziałem niespójne twierdzenia dotyczące tego, co jest najszybsze w skracaniu czasu od daty w SQL Server, a niektórzy nawet powiedzieli, że przeprowadzali testy, ale moje doświadczenie było inne. Zróbmy więc bardziej rygorystyczne testy i niech każdy ma skrypt, więc jeśli popełnię jakiś błąd, ludzie mogą mnie poprawić.
Konwersje zmiennoprzecinkowe nie są dokładne
Po pierwsze, trzymałbym się z daleka od konwertowania datetime
do float
, ponieważ nie konwertuje poprawnie. Możesz ujść na sucho z dokładnym usuwaniem czasu, ale myślę, że używanie go jest złym pomysłem, ponieważ domyślnie informuje programistów, że jest to bezpieczna operacja, a tak nie jest . Spójrz:
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
Nie jest to coś, czego powinniśmy uczyć ludzi w naszym kodzie lub w naszych przykładach online.
Poza tym nie jest to nawet najszybszy sposób!
Dowód — testowanie wydajności
Jeśli chcesz samodzielnie wykonać kilka testów, aby zobaczyć, jak różne metody naprawdę się stosują, będziesz potrzebować tego skryptu konfiguracyjnego, aby przeprowadzić testy w dół:
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
Pamiętaj, że spowoduje to utworzenie tabeli 427,57 MB w Twojej bazie danych, a jej uruchomienie zajmie około 15-30 minut. Jeśli twoja baza danych jest mała i ustawiona na 10% wzrost, zajmie to więcej czasu niż jeśli najpierw będziesz wystarczająco duży.
Teraz przejdźmy do skryptu testowania wydajności. Należy pamiętać, że celowe jest nie zwracanie wierszy klientowi, ponieważ jest to szalenie kosztowne w przypadku 26 milionów wierszy i ukryłoby różnice w wydajności między metodami.
Wyniki wydajności
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;
Niektóre analizy rambling
Kilka uwag na ten temat. Po pierwsze, jeśli wykonujesz tylko grupowanie według lub porównanie, nie ma potrzeby konwertowania z powrotem na datetime
. Możesz więc zaoszczędzić trochę procesora, unikając tego, chyba że potrzebujesz końcowej wartości do celów wyświetlania. Możesz nawet GROUP BY nieprzekonwertowaną wartość i umieścić konwersję tylko w klauzuli 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)
Zobacz także, jak konwersje numeryczne zajmują tylko nieco więcej czasu, aby przekonwertować z powrotem na datetime
, ale varchar
konwersja prawie się podwaja? Pokazuje to, jaka część procesora jest przeznaczana na obliczanie dat w zapytaniach. Niektóre części użycia procesora nie wymagają obliczania dat i wydaje się, że w powyższych zapytaniach jest to coś zbliżonego do 19875 ms. Następnie konwersja wymaga dodatkowej kwoty, więc jeśli są dwie konwersje, ta kwota jest zużywana około dwa razy.
Więcej badań pokazuje, że w porównaniu z Convert(, 112)
, Convert(, 101)
zapytanie ma dodatkowy koszt procesora (ponieważ używa dłuższego varchar
?), ponieważ druga konwersja z powrotem do date
nie kosztuje tyle, co początkowa konwersja do varchar
, ale z Convert(, 112)
jest bliższy temu samemu kosztowi bazowemu procesora 20000 ms.
Oto te obliczenia dotyczące czasu procesora, których użyłem do powyższej analizy:
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
-
okrągły to czas procesora na podróż w obie strony z powrotem do
datetime
. -
pojedynczy to czas procesora dla pojedynczej konwersji na alternatywny typ danych (ten, który ma efekt uboczny usunięcia części czasu).
-
podstawa to obliczenie odejmowania od
single
różnica między dwoma wywołaniami:single - (round - single)
. Jest to liczba, która zakłada konwersję do iz tego typu danych orazdatetime
jest mniej więcej taki sam w obu kierunkach. Wygląda na to, że to założenie nie jest idealne, ale jest bliskie, ponieważ wszystkie wartości są bliskie 20000 ms z jednym wyjątkiem.
Jeszcze jedną interesującą rzeczą jest to, że koszt bazowy jest prawie równy jednemu Convert(date)
metoda (która musi kosztować prawie 0, ponieważ serwer może wewnętrznie wyodrębnić część dnia całkowitego z pierwszych czterech bajtów datetime
typ danych).
Wniosek
Wygląda więc na to, że jednokierunkowy varchar
metoda konwersji zajmuje około 1,8 μs, a jednokierunkowy DateDiff
metoda trwa około 0,18 μs. Opieram to na najbardziej konserwatywnym czasie „podstawowego procesora” w moich testach, wynoszącym łącznie 18458 ms dla 25 920 000 wierszy, więc 23218 ms / 25920000 =0,18 μs. Pozorna 10-krotna poprawa wydaje się być duża, ale szczerze mówiąc jest dość mała, dopóki nie masz do czynienia z setkami tysięcy wierszy (617 tys. wierszy =1 sekunda oszczędności).
Nawet biorąc pod uwagę tę niewielką absolutną poprawę, moim zdaniem, DateAdd
Metoda wygrywa, ponieważ jest najlepszą kombinacją wydajności i przejrzystości. Odpowiedź, która wymaga „magicznej liczby” 0.50000004
kiedyś kogoś ugryzie (pięć zer czy sześć???), a do tego trudniej to zrozumieć.
Dodatkowe uwagi
Kiedy będę miał trochę czasu, zmienię 0.50000004
do '12:00:00.003'
i zobacz, jak to działa. Jest konwertowany na ten sam datetime
wartość i jest mi o wiele łatwiej ją zapamiętać.
Dla zainteresowanych powyższe testy zostały przeprowadzone na serwerze, na którym @@Version zwraca następujące dane:
Microsoft SQL Server 2008 (RTM) — 10.0.1600.22 (Intel X86) 9 lipca 2008 14:43:34 Prawa autorskie (c) 1988-2008 Microsoft Corporation Standard Edition w systemie Windows NT 5.2 (kompilacja 3790:Service Pack 2)