Làm cách nào tôi có thể cắt một datetime trong SQL Server?


280

Cách tốt nhất để cắt ngắn một giá trị thời gian (như loại bỏ giờ phút và giây) trong SQL Server 2008 là gì?

Ví dụ:

declare @SomeDate datetime = '2009-05-28 16:30:22'
select trunc_date(@SomeDate)

-----------------------
2009-05-28 00:00:00.000

Câu trả lời:


494

Điều này tiếp tục thường xuyên thu thập thêm phiếu bầu, thậm chí vài năm sau đó, và vì vậy tôi cần cập nhật nó cho các phiên bản hiện đại của Sql Server. Đối với Sql Server 2008 trở lên, thật đơn giản:

cast(getDate() As Date)

Lưu ý rằng ba đoạn cuối gần phía dưới vẫn được áp dụng và bạn thường cần lùi lại một bước và tìm cách tránh diễn viên ở vị trí đầu tiên.

Nhưng có nhiều cách khác để thực hiện điều này, quá. Dưới đây là những phổ biến nhất.

Cách chính xác (mới kể từ Sql Server 2008):

cast(getdate() As Date)

Cách chính xác (cũ):

dateadd(dd, datediff(dd,0, getDate()), 0)

Bây giờ nó đã cũ hơn, nhưng nó vẫn đáng để biết vì nó cũng có thể dễ dàng thích nghi với các thời điểm khác, như khoảnh khắc đầu tiên của tháng, phút, giờ hoặc năm.

Cách chính xác này sử dụng các chức năng được ghi lại là một phần của tiêu chuẩn ansi và được đảm bảo để hoạt động, nhưng nó có thể chậm hơn một chút. Nó hoạt động bằng cách tìm xem có bao nhiêu ngày từ ngày 0 đến ngày hiện tại và thêm nhiều ngày trở lại ngày 0. Nó sẽ hoạt động bất kể thời gian của bạn được lưu trữ như thế nào và bất kể địa điểm của bạn là gì.

Cách nhanh chóng:

cast(floor(cast(getdate() as float)) as datetime)

Điều này hoạt động vì các cột datetime được lưu trữ dưới dạng giá trị nhị phân 8 byte. Truyền cho chúng nổi, đặt sàn để loại bỏ phân số và phần thời gian của các giá trị sẽ biến mất khi bạn chuyển chúng trở lại datetime. Tất cả chỉ là một chút thay đổi mà không có logic phức tạp và nó rất nhanh.

Xin lưu ý rằng điều này phụ thuộc vào chi tiết triển khai Microsoft có thể thay đổi bất cứ lúc nào, ngay cả trong bản cập nhật dịch vụ tự động. Nó cũng không di động lắm. Trong thực tế, việc triển khai này rất khó có thể sớm thay đổi, nhưng điều quan trọng là phải nhận thức được sự nguy hiểm nếu bạn chọn sử dụng nó. Và bây giờ chúng ta có tùy chọn để chọn một ngày, điều đó hiếm khi cần thiết.

Cách sai:

cast(convert(char(11), getdate(), 113) as datetime)

Cách sai hoạt động bằng cách chuyển đổi thành một chuỗi, cắt ngắn chuỗi và chuyển đổi lại thành một datetime. Điều đó là sai , vì hai lý do: 1) nó có thể không hoạt động trên tất cả các địa phương và 2) đó là cách chậm nhất có thể để làm điều này ... và không chỉ một chút; nó giống như một thứ tự cường độ hoặc chậm hơn hai so với các tùy chọn khác.


Cập nhật Điều này đã nhận được một số phiếu gần đây và vì vậy tôi muốn thêm vào đó vì kể từ khi tôi đăng bài này, tôi đã thấy một số bằng chứng khá chắc chắn rằng Sql Server sẽ tối ưu hóa sự khác biệt hiệu suất giữa cách "chính xác" và cách "nhanh" , có nghĩa là bây giờ bạn nên ủng hộ trước đây.

Trong cả hai trường hợp, bạn muốn viết các truy vấn của mình để tránh phải làm điều này ngay từ đầu . Rất hiếm khi bạn làm công việc này trên cơ sở dữ liệu.

Ở hầu hết các nơi, cơ sở dữ liệu đã là nút cổ chai của bạn. Nói chung, máy chủ đắt nhất để thêm phần cứng để cải thiện hiệu suất và là máy chủ khó nhất để có được những bổ sung đó (ví dụ: bạn phải cân bằng đĩa với bộ nhớ). Đó cũng là khó khăn nhất để mở rộng ra bên ngoài, cả về mặt kỹ thuật và từ quan điểm kinh doanh; Về mặt kỹ thuật, việc thêm một máy chủ ứng dụng hoặc web dễ dàng hơn nhiều so với máy chủ cơ sở dữ liệu và ngay cả khi đó là sai, bạn không phải trả 20.000 đô la cho mỗi giấy phép máy chủ cho IIS hoặc apache.

Điểm tôi đang cố gắng thực hiện là bất cứ khi nào có thể bạn nên thực hiện công việc này ở cấp ứng dụng. Lần duy nhất bạn nên thấy mình cắt ngắn một datetime trên Sql Server là khi bạn cần nhóm theo ngày và thậm chí sau đó bạn có thể nên có một cột bổ sung được thiết lập dưới dạng cột được tính toán, duy trì tại thời điểm chèn / cập nhật hoặc duy trì trong logic ứng dụng. Lấy công việc nặng về chỉ số, cpu này ra khỏi cơ sở dữ liệu của bạn.


6
"cách nhanh" vẫn là cách nhanh nhất cho sql 2008 theo điểm chuẩn tôi vừa chạy
Sam Saffron

3
FYI: stackoverflow.com/q/1177449/27535stackoverflow.com/q/133081/27535 dateadd / dateiff "thắng ...". Đối với một biến duy nhất, ai quan tâm tất nhiên và một người hy vọng rằng bạn đã tính toán các cột hoặc hơn một triệu hàng :-)
gbn

9
Cách "chính xác" này chỉ vô tình hoạt động. Cách nó được viết giống như cú pháp của DateAdd là (khoảng, ngày, tăng), nhưng không phải vậy. Đó là (khoảng, tăng, ngày). Tôi đã vấp phải điều này khi tôi cố gắng cắt ngắn một ngày đến đầu tháng: CHỌN DATEADD (m, 0, DATEDIFF (m, 0, GETDATE ())) không hoạt động, nhưng CHỌN DATEADD (m, DATEDIFF (m, 0, GETDATE ()), 0) nào. Ít nhất, đây là những gì tôi thấy trong năm 2008R2.
Kelly Cline

1
@Kelly năm 2008R2, tại sao không cast(getdate() as date)?
Joel Coehoorn

2
Tất cả đều hoạt động trên cột datetime. getdate()đây là điểm thay thế cho bất kỳ nguồn thời gian nào bạn có thể có.
Joel Coehoorn

44

Chỉ dành cho SQL Server 2008

CAST(@SomeDateTime AS Date) 

Sau đó chuyển nó trở lại datetime nếu bạn muốn

CAST(CAST(@SomeDateTime AS Date) As datetime)

Điểm hay: Tôi vẫn còn trên 2005 và vì vậy trong năm 2008, đây có lẽ là cách "chính xác" mới và thậm chí có thể phù hợp với hiệu suất của cách "nhanh".
Joel Coehoorn

1
Hiệu suất của cách mới này thậm chí còn nhanh hơn cách "nhanh".
ErikE

21

Chỉ vì một câu trả lời đầy đủ hơn, đây là một cách làm việc để cắt bớt bất kỳ phần nào trong ngày và bao gồm cả phút (thay thế GETDATE()bằng ngày để cắt ngắn).

Điều này khác với câu trả lời được chấp nhận ở chỗ bạn có thể sử dụng không chỉ dd(ngày), mà bất kỳ phần nào trong ngày (xem tại đây ):

dateadd(minute, datediff(minute, 0, GETDATE()), 0)

Lưu ý rằng trong biểu thức trên, 0là một ngày không đổi vào đầu năm (1900-01-01). Nếu bạn cần cắt ngắn thành các phần nhỏ hơn, chẳng hạn như giây hoặc mili giây, bạn cần lấy một ngày không đổi gần với ngày bị cắt bớt để tránh tràn.


1
Điều này thật hữu ích. Tôi đã tìm mọi cách để cắt ngắn thời gian ngày ở một nơi thấp hơn cả ngày.
Michael - Clay Shirky đâu

1
@Michael, cảm ơn bạn đã phản hồi, thật tốt khi biết rằng nó đã giúp bạn!
Lucero

1
+1 điều này sẽ có nhiều upvote hơn, đó là một câu trả lời tuyệt vời mở rộng cho câu trả lời được chọn.
jtate

1
Để internet biết, bạn không cần phải giới hạn thời gian hẹn hò đầy đủ. Đây là một ví dụ cho các khoảng thời gian 15 phút, sử dụng phép chia số nguyên:dateadd(minute, datediff(minute, 0, GETDATE()) / 15 * 15, 0)
Michael - Clay's Clay Shirky

7

Đoạn mã tôi tìm thấy trên web khi tôi phải làm điều này là:

 dateadd(dd,0, datediff(dd,0, YOURDATE))
 e.g.
 dateadd(dd,0, datediff(dd,0, getDate()))

Tôi vào năm 2005, nhưng tôi nghĩ năm 2008 có một số chức năng mới cho việc này ??
KM.

2
Khéo léo! Tôi đã phải dùng đến việc phân tách các ngày tháng và sử dụng xử lý chuỗi để đặt chúng lại với nhau. Có thể không liên quan, nhưng SQL2008 có kiểu dữ liệu chỉ ngày thuần túy mà không có yếu tố thời gian.
Frans

1
Và lưu ý rằng bạn có các toán hạng DateAdd lẫn lộn, đó là DateAdd(dd, DateDiff(...), 0). Điều này có thể cắn bạn nếu bạn không cẩn thận.
ErikE

1

Trong SQl 2005, hàm trunc_date của bạn có thể được viết như thế này.

(1)

CREATE FUNCTION trunc_date(@date DATETIME)
RETURNS DATETIME
AS
BEGIN
    CAST(FLOOR( CAST( @date AS FLOAT ) )AS DATETIME)
END

Phương pháp đầu tiên sạch sẽ hơn nhiều. Nó chỉ sử dụng 3 lệnh gọi phương thức bao gồm CAST () cuối cùng và không thực hiện nối chuỗi, đây là một phép cộng tự động. Hơn nữa, không có loại phôi lớn ở đây. Nếu bạn có thể tưởng tượng rằng tem Ngày / Giờ có thể được biểu diễn, thì chuyển đổi từ ngày sang số và quay lại ngày là một quá trình khá dễ dàng.

(2)

CREATE FUNCTION trunc_date(@date DATETIME)
RETURNS DATETIME
AS
BEGIN
      SELECT CONVERT(varchar, @date,112)
END

Nếu bạn lo lắng về việc triển khai datetimes (2) hoặc (3) của microsoft có thể ổn.

(3)

CREATE FUNCTION trunc_date(@date DATETIME)
RETURNS DATETIME
AS
BEGIN
SELECT CAST((STR( YEAR( @date ) ) + '/' +STR( MONTH( @date ) ) + '/' +STR( DAY(@date ) )
) AS DATETIME
END

Thứ ba, phương pháp dài dòng hơn. Điều này đòi hỏi phải chia ngày thành các phần năm, tháng và ngày của nó, đặt chúng lại với nhau theo định dạng "yyyy / mm / dd", sau đó chuyển ngày đó trở lại. Phương thức này bao gồm 7 lệnh gọi phương thức bao gồm CAST () cuối cùng, chưa kể đến nối chuỗi.


1
CONVERT(DATE, <yourdatetime>) or CONVERT(DATE, GetDate()) or CONVERT(DATE, CURRENT_TIMESTAMP)


0

Đối với những người bạn đến đây đang tìm cách cắt ngắn trường DATETIME thành một thứ ít hơn cả ngày, ví dụ mỗi phút, bạn có thể sử dụng cách này:

SELECT CAST(FLOOR(CAST(GETDATE() AS FLOAT)) + (FLOOR((CAST(GETDATE() AS FLOAT) - FLOOR(CAST(GETDATE() AS FLOAT))) * 1440.0) + (3.0/86400000.0)) / 1440.0 AS DATETIME)

Vì vậy, nếu hôm nay là 2010-11-26 14:54:43.123sau này sẽ trở lại 2010-11-26 14:54:00.000.

Để thay đổi khoảng thời gian mà nó thực hiện, thay thế 1440.0 bằng số lượng khoảng thời gian trong một ngày, ví dụ:

24hrs          =   24.0  (for every hour)
24hrs / 0.5hrs =   48.0  (for every half hour)
24hrs / (1/60) = 1440.0  (for every minute)

(Luôn đặt .0dấu chấm hết để hoàn toàn thả nổi.)


Đối với những người bạn tự hỏi những gì (3.0/86400000)là cho trong tính toán của tôi, SQL Server 2005 dường như không diễn viên từ FLOATđến DATETIMEmột cách chính xác, vì vậy đây thêm 3 mili giây trước khi sàn nó.


1
Hãy cẩn thận với các lỗi làm tròn do giới hạn độ chính xác của dấu phẩy động ... tuy nhiên, điều này không hoạt động với datetime2kiểu dữ liệu.
Lucero

Đối với Giờ, CHỌN DATEADD (giờ, DATEDIFF (giờ, 0, GETDATE ()), 0) cũng hoạt động. Phút cũng vậy, nhưng Thứ hai sẽ dẫn đến tràn.
Kelly Cline

Đúc để nổi và trở lại datetime không hoạt động chính xác .
ErikE

0

Truy vấn này sẽ cung cấp cho bạn kết quả tương đương với trunc(sysdate)trong Oracle.

SELECT  * 
FROM    your_table
WHERE   CONVERT(varchar(12), your_column_name, 101)
      = CONVERT(varchar(12), GETDATE(), 101)

Hi vọng điêu nay co ich!


0

Bạn cũng có thể trích xuất ngày using Substringtừ biến datetime và chuyển trở lại datetime sẽ bỏ qua phần thời gian.

declare @SomeDate datetime = '2009-05-28 16:30:22'
SELECT cast(substring(convert(varchar(12),@SomeDate,111),0,12) as Datetime) 

Ngoài ra, bạn có thể truy cập các phần của biến datetime và hợp nhất chúng vào một ngày rút ngắn cấu trúc, đại loại như thế này:

SELECT cast(DATENAME(year, @Somedate) + '-' + 
       Convert(varchar(2),DATEPART(month, @Somedate)) + '-' +
       DATENAME(day, @Somedate) 
       as datetime)

0

Oracle:

TRUNC(SYSDATE, 'MONTH')

Máy chủ SQL:

DATEADD(DAY, - DATEPART(DAY, DateField) + 1, DateField)

Có thể được sử dụng tương tự để cắt ngắn phút hoặc giờ kể từ ngày.



Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.