Sqlserver
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> Sqlserver

Làm cách nào để loại bỏ phần thời gian của giá trị datetime (SQL Server)?

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)



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. trường đếm to_sql pyodbc không chính xác hoặc lỗi cú pháp

  2. Vòng lặp SQL Server - làm cách nào để tôi lặp qua một tập hợp các bản ghi

  3. SQL Server Parallel Backup Restore -2

  4. Cách sử dụng IF ... THEN Logic trong SQL Server

  5. Cách thay đổi một cột từ Null thành Not Null trong SQL Server