SQL Server 2008 i nowsze
W SQL Server 2008 i nowszych oczywiście najszybszym sposobem jest Convert(date, @date)
. Można to odrzucić z powrotem do datetime
lub, datetime2
jeśli to konieczne.
Co jest naprawdę najlepsze w SQL Server 2005 i starszych?
Widziałem niespójne stwierdzenia dotyczące najszybszego skrócenia czasu od daty w SQL Server, a niektórzy nawet powiedzieli, że przeprowadzili testy, ale moje doświadczenie było inne. Zróbmy więc bardziej rygorystyczne testy i pozwólmy każdemu mieć skrypt, więc jeśli popełnię jakieś błędy, ludzie będą mogli mnie poprawić.
Konwersje zmiennoprzecinkowe nie są dokładne
Po pierwsze, nie chciałbym konwertować datetime
na float
, ponieważ nie konwertuje poprawnie. Możesz uciec od dokładnego usunięcia czasu, ale myślę, że używanie go jest złym pomysłem, ponieważ pośrednio komunikuje programistom, że jest to operacja bezpieczna, a tak nie jest . Spójrz:
declare @d datetime;
set @d = '2010-09-12 00:00:00.003';
select Convert(datetime, Convert(float, @d));
To nie jest coś, czego powinniśmy uczyć ludzi w naszym kodzie lub w naszych przykładach online.
Nie jest to nawet najszybszy sposób!
Dowód - testowanie wydajności
Jeśli chcesz samodzielnie wykonać kilka testów, aby zobaczyć, jak naprawdę zestawiają się różne metody, będziesz potrzebować tego skryptu instalacyjnego, aby uruchomić testy dalej:
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;
Należy pamiętać, że spowoduje to utworzenie tabeli o wielkości 427,57 MB w Twojej bazie danych i zajmie około 15-30 minut. Jeśli Twoja baza danych jest mała i ustawiona na 10% wzrostu, zajmie to więcej czasu niż w przypadku wystarczająco dużego rozmiaru.
Teraz czas na rzeczywisty skrypt do testowania wydajności. Należy pamiętać, że celowe jest, aby nie zwracać wierszy z powrotem do klienta, ponieważ jest to szalenie drogie w przypadku 26 milionów wierszy i mogłoby ukryć różnice w wydajności między metodami.
Wyniki wydajności
set statistics time on;
GO
declare
@dd date,
@d datetime,
@di int,
@df float,
@dv varchar(10);
select @d = CONVERT(date, Tm) from AllDay;
select @d = CAST(Tm - 0.50000004 AS int) from AllDay;
select @d = DATEDIFF(DAY, 0, Tm) from AllDay;
select @d = FLOOR(CAST(Tm as float)) from AllDay;
select @d = CONVERT(VARCHAR(8), Tm, 112) from AllDay;
select @d = CONVERT(CHAR(8), Tm, 112) from AllDay;
select @d = CONVERT(VARCHAR(10), Tm, 101) from AllDay;
select @dd = Tm from AllDay;
select @di = CAST(Tm - 0.50000004 AS int) from AllDay;
select @di = DATEDIFF(DAY, 0, Tm) from AllDay;
select @df = FLOOR(CAST(Tm as float)) from AllDay;
select @dv = CONVERT(VARCHAR(8), Tm, 112) from AllDay;
select @dv = CONVERT(CHAR(8), Tm, 112) from AllDay;
select @dv = CONVERT(VARCHAR(10), Tm, 101) from AllDay;
GO
set statistics time off;
Trochę wędrującej analizy
Kilka uwag na ten temat. Po pierwsze, jeśli wykonujesz tylko GROUP BY lub porównanie, nie ma potrzeby powrotu do datetime
. Możesz więc zaoszczędzić trochę procesora, unikając tego, chyba że potrzebujesz ostatecznej 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 też, że konwersja z powrotem do konwersji numerycznej zajmuje tylko nieco więcej czasu datetime
, ale varchar
konwersja prawie się podwaja? Ujawnia to część procesora przeznaczoną na obliczanie daty w zapytaniach. Istnieją części wykorzystania procesora, które nie obejmują obliczania daty i wydaje się, że w powyższych zapytaniach jest to coś zbliżonego do 19875 ms. Wtedy konwersja wymaga dodatkowej kwoty, więc jeśli są dwie konwersje, ta kwota jest zużyta około dwa razy.
Więcej badań ujawnia, że w porównaniu Convert(, 112)
z Convert(, 101)
zapytaniem jest dodatkowy koszt procesora (ponieważ zużywa dłużej varchar
?), Ponieważ druga konwersja z powrotem do date
nie kosztuje tyle, co początkowa konwersja do varchar
, ale z Convert(, 112)
nią jest bliższa tym samym 20000 ms podstawowy koszt procesora.
Oto te obliczenia 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
round to czas procesora na podróż w obie strony do datetime
.
single to czas procesora dla pojedynczej konwersji na alternatywny typ danych (taki, który ma efekt uboczny usunięcia części czasu).
Podstawa jest obliczenie odjęcie od single
różnicy pomiędzy dwoma wywołaniami: single - (round - single)
. Jest to liczba, która zakłada konwersję do iz tego typu danych i datetime
jest w przybliżeniu taka sama w obu kierunkach. Wydaje się, że to założenie nie jest doskonałe, ale jest bliskie, ponieważ wszystkie wartości są bliskie 20000 ms, z jednym tylko wyjątkiem.
Jeszcze jedną interesującą rzeczą jest to, że koszt podstawowy jest prawie równy kosztowi pojedynczej Convert(date)
metody (który musi być prawie zerowy, ponieważ serwer może wewnętrznie wyodrębnić część całkowitą dnia z pierwszych czterech bajtów datetime
typu danych).
Wniosek
Wygląda więc na to, że varchar
metoda konwersji jednokierunkowej zajmuje około 1,8 μs, a DateDiff
metoda jednokierunkowa około 0,18 μs. Opieram to na najbardziej konserwatywnym czasie „bazowego procesora” w moich testach, który wynosi łącznie 18458 ms dla 25 920 000 wierszy, czyli 23218 ms / 25920000 = 0,18 μs. Pozorna 10-krotna poprawa wydaje się duża, ale szczerze mówiąc jest dość niewielka, 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 najlepszym połączeniem wydajności i przejrzystości. Odpowiedź, która wymaga „magicznej liczby”, 0.50000004
pewnego dnia kogoś ugryzie (pięć zer lub sześć ???), a ponadto będzie trudniejsza do zrozumienia.
Dodatkowe uwagi
Kiedy będę miał trochę czasu, zmienię 0.50000004
się '12:00:00.003'
i zobaczę, jak to działa. Jest konwertowany na tę samą datetime
wartość i uważam, że jest znacznie łatwiejszy do zapamiętania.
Dla zainteresowanych powyższe testy zostały uruchomione na serwerze, na którym @@ Version zwraca:
Microsoft SQL Server 2008 (RTM) - 10.0.1600.22 (Intel X86) 9 lipca 2008 14:43:34 Copyright (c) 1988-2008 Microsoft Corporation Standard Edition w systemie Windows NT 5.2 (kompilacja 3790: dodatek Service Pack 2)