SQL Server 2008 trở lên
Trong SQL Server 2008 trở lên, tất nhiên cách nhanh nhất là Convert(date, @date)
. Điều này có thể được truyền trở lại datetime
hoặc datetime2
nếu cần.
Điều gì thực sự tốt nhất trong SQL Server 2005 trở lên?
Tôi đã thấy các tuyên bố không nhất quán về việc cắt ngắn thời gian từ một ngày trong SQL Server là gì nhanh nhất và một số người thậm chí còn nói rằng họ đã thử nghiệm, nhưng trải nghiệm của tôi thì khác. Vì vậy, hãy thực hiện một số thử nghiệm nghiêm ngặt hơn và để mọi người có tập lệnh để nếu tôi mắc bất kỳ lỗi nào, mọi người có thể sửa cho tôi.
Chuyển đổi nổi không chính xác
Đầu tiên, tôi sẽ tránh chuyển đổi datetime
thành float
, bởi vì nó không chuyển đổi chính xác. Bạn có thể không cần thực hiện thao tác loại bỏ thời gian một cách chính xác, nhưng tôi nghĩ không nên sử dụng nó vì nó ngầm thông báo với các nhà phát triển rằng đây là một thao tác an toàn và không phải . Hãy xem:
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
Đây không phải là điều mà chúng ta nên dạy mọi người về mã của chúng ta hoặc trong các ví dụ trực tuyến của chúng ta.
Ngoài ra, nó thậm chí không phải là cách nhanh nhất!
Bằng chứng - Kiểm tra hiệu suất
Nếu bạn muốn tự mình thực hiện một số thử nghiệm để xem các phương pháp khác nhau thực sự xếp chồng lên nhau như thế nào, thì bạn sẽ cần tập lệnh thiết lập này để chạy các thử nghiệm sâu hơn:
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
Xin lưu ý rằng điều này tạo ra một bảng 427,57 MB trong cơ sở dữ liệu của bạn và sẽ mất khoảng 15-30 phút để chạy. Nếu cơ sở dữ liệu của bạn nhỏ và được đặt ở mức tăng trưởng 10% thì sẽ mất nhiều thời gian hơn so với khi bạn kích thước đủ lớn trước.
Bây giờ là kịch bản kiểm tra hiệu suất thực tế. Xin lưu ý rằng mục đích là không trả lại các hàng cho máy khách vì điều này rất đắt trên 26 triệu hàng và sẽ che giấu sự khác biệt về hiệu suất giữa các phương pháp.
Kết quả Hiệu suất
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;
Một số phân tích lan man
Một số lưu ý về điều này. Trước hết, nếu chỉ thực hiện GROUP BY hoặc so sánh, không cần phải chuyển đổi trở lại datetime
. Vì vậy, bạn có thể tiết kiệm một số CPU bằng cách tránh điều đó, trừ khi bạn cần giá trị cuối cùng cho mục đích hiển thị. Bạn thậm chí có thể NHÓM THEO giá trị chưa được chuyển đổi và chỉ đặt chuyển đổi trong mệnh đề CHỌN:
select Convert(datetime, DateDiff(dd, 0, Tm))
from (select '2010-09-12 00:00:00.003') X (Tm)
group by DateDiff(dd, 0, Tm)
Ngoài ra, hãy xem cách các chuyển đổi số chỉ mất thêm một chút thời gian để chuyển đổi trở lại datetime
, nhưng varchar
chuyển đổi gần như tăng gấp đôi? Điều này cho thấy phần của CPU được dành cho việc tính toán ngày tháng trong các truy vấn. Có một số phần của việc sử dụng CPU không liên quan đến tính toán ngày tháng và điều này dường như là một cái gì đó gần 19875 ms trong các truy vấn trên. Sau đó, chuyển đổi cần một số tiền bổ sung, vì vậy nếu có hai chuyển đổi, số tiền đó sẽ được sử dụng gần gấp đôi.
Kiểm tra nhiều hơn cho thấy rằng so với Convert(, 112)
, Convert(, 101)
truy vấn có thêm một số chi phí CPU (vì nó sử dụng varchar
dài hơn ?), bởi vì lần chuyển đổi thứ hai trở lại date
không tốn nhiều chi phí như chuyển đổi ban đầu thành varchar
, nhưng với Convert(, 112)
nó gần với cùng một mức chi phí cơ bản 20000 ms cho CPU.
Dưới đây là những tính toán về thời gian CPU mà tôi đã sử dụng cho phân tích ở trên:
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
-
vòng là thời gian CPU cho chuyến đi khứ hồi về
datetime
. -
đơn là thời gian CPU cho một lần chuyển đổi sang kiểu dữ liệu thay thế (kiểu dữ liệu có tác dụng phụ là loại bỏ phần thời gian).
-
căn cứ là phép tính trừ từ
single
sự khác biệt giữa hai lệnh gọi:single - (round - single)
. Đó là một hình vẽ quả bóng giả định chuyển đổi đến và từ loại dữ liệu đó vàdatetime
gần giống nhau theo cả hai hướng. Có vẻ như giả định này không hoàn hảo nhưng gần đúng vì tất cả các giá trị đều gần 20000 ms chỉ có một ngoại lệ.
Một điều thú vị nữa là chi phí cơ bản gần bằng với một Convert(date)
duy nhất (mà phải có chi phí gần như bằng 0, vì máy chủ có thể trích xuất nội bộ phần ngày số nguyên ngay trong bốn byte đầu tiên của datetime
kiểu dữ liệu).
Kết luận
Vì vậy, nó trông giống như một hướng duy nhất varchar
phương pháp chuyển đổi mất khoảng 1,8 μs và DateDiff
một hướng phương pháp mất khoảng 0,18 μs. Tôi dựa trên điều này dựa trên thời gian "CPU cơ sở" bảo thủ nhất trong thử nghiệm của tôi là 18458 ms tổng cộng cho 25,920,000 hàng, vì vậy 23218 ms / 25920000 =0,18 μs. Sự cải thiện 10x rõ ràng có vẻ như rất nhiều, nhưng nói thật là nó khá nhỏ cho đến khi bạn xử lý hàng trăm nghìn hàng (617 nghìn hàng =tiết kiệm 1 giây).
Ngay cả với cải tiến tuyệt đối nhỏ này, theo ý kiến của tôi, DateAdd
phương pháp chiến thắng vì nó là sự kết hợp tốt nhất giữa hiệu suất và sự rõ ràng. Câu trả lời yêu cầu "số ma thuật" là 0.50000004
sẽ cắn ai đó vào một ngày nào đó (năm số 0 hay sáu ???), cộng với việc khó hiểu hơn.
Ghi chú bổ sung
Khi có thời gian, tôi sẽ thay đổi 0.50000004
thành '12:00:00.003'
và xem nó hoạt động như thế nào. Nó được chuyển đổi thành cùng một datetime
giá trị và tôi thấy nó dễ nhớ hơn nhiều.
Đối với những người quan tâm, các thử nghiệm ở trên đã được chạy trên máy chủ mà @@ Phiên bản trả về như sau:
Microsoft SQL Server 2008 (RTM) - 10.0.1600.22 (Intel X86) ngày 9 tháng 7 năm 2008 14:43:34 Bản quyền (c) 1988-2008 Microsoft Corporation Standard Edition trên Windows NT 5.2 (Bản dựng 3790:Gói dịch vụ 2)