Câu trả lời:
Vì bạn đang sử dụng datetimekiểu dữ liệu, bạn cần hiểu cách máy chủ sql làm tròn dữ liệu datetime.
╔═══════════╦═════╦═════════════════════════════╦═════════════════════════════╦══════════╦═══════════╗
║ Name ║ sn ║ Minimum value ║ Maximum value ║ Accuracy ║ Storage ║
╠═══════════╬═════╬═════════════════════════════╬═════════════════════════════╬══════════╬═══════════╣
║ datetime ║ dt ║ 1753-01-01 00:00:00.000 ║ 9999-12-31 23:59:59.997 ║ 3.33 ms ║ 8 bytes ║
║ datetime2 ║ dt2 ║ 0001-01-01 00:00:00.0000000 ║ 9999-12-31 23:59:59.9999999 ║ 100ns ║ 6-8 bytes ║
╚═══════════╩═════╩═════════════════════════════╩═════════════════════════════╩══════════╩═══════════╝
Sử dụng truy vấn bên dưới, bạn có thể dễ dàng thấy vấn đề làm tròn máy chủ sql khi bạn sử dụng DATETIME
kiểu dữ liệu.
select '2015-07-27 00:00:00.000' as Original_startDateTime,
convert(datetime ,'2015-07-27 00:00:00.000') as startDateTime,
'2015-07-27 23:59:59.999' as Original_endDateTime,
convert(datetime ,'2015-07-27 23:59:59.999') as endDateTime,
'2015-07-27 00:00:00.000' as Original_startDateTime2,
convert(datetime2 ,'2015-07-27 00:00:00.000') as startDateTime2, -- default precision is 7
'2015-07-27 23:59:59.999' as Original_endDateTime2,
convert(datetime2 ,'2015-07-27 23:59:59.999') as endDateTime2 -- default precision is 7
DATETIME2
đã xuất hiện từ SQL Server 2008, vì vậy hãy bắt đầu sử dụng nó thay vì DATETIME
. Đối với tình huống của bạn, bạn có thể sử dụng datetime2
với độ chính xác là 3 số thập phân, vd datetime2(3)
.
Lợi ích của việc sử dụng datetime2
:
datetime
hỗ trợ chỉ có 3 chữ số thập phân .. và do đó bạn sẽ thấy vấn đề làm tròn kể từ khi theo mặc định datetime
vòng gần nhất .003 seconds
với gia số của .000
, .003
hoặc .007
giây.datetime2
là chính xác hơn nhiều datetime
và datetime2
cung cấp cho bạn quyền kiểm soát DATE
và TIME
trái ngược với datetime
.Tài liệu tham khảo :
DateTime2
so với DateTime
: a. Đối với - trường hợp - rộng lớn - đa số - thực tế - thế giới - sử dụng - lợi ích của phần DateTime2
lớn <chi phí. Xem: stackoverflow.com/questions/1334143/ b. Đó không phải là vấn đề gốc ở đây. Xem bình luận tiếp theo.
datetime3
có thêm 70 (so với 7) chữ số chính xác được thêm vào?). Thực hành tốt nhất là sử dụng một giá trị trong đó độ chính xác không thành vấn đề, tức là < bắt đầu của giây tiếp theo, phút, giờ hoặc ngày so với <= kết thúc của giây trước, phút, giờ hoặc ngày.
Như một số người khác đã đề cập trong các bình luận và các câu trả lời khác cho câu hỏi của bạn, vấn đề cốt lõi 2015-07-27 23:59:59.999
đang được 2015-07-28 00:00:00.000
SQL Server làm tròn . Theo tài liệu cho DATETIME:
Phạm vi thời gian - 00:00:00 đến 23: 59: 59.997
Lưu ý rằng phạm vi thời gian không bao giờ có thể được .999
. Hơn nữa trong tài liệu này, nó chỉ định các quy tắc làm tròn mà SQL Server sử dụng cho chữ số có nghĩa ít nhất.
Lưu ý rằng chữ số có nghĩa nhỏ nhất chỉ có thể có một trong ba giá trị tiềm năng: "0", "3" hoặc "7".
Có một số giải pháp / giải pháp cho việc này mà bạn có thể sử dụng.
-- Option 1
SELECT
*
FROM A
WHERE posted_date >= '2015-07-27 00:00:00.000'
AND posted_date < '2015-07-28 00:00:00.000' --Round up and remove equality
-- Option 2
SELECT
*
FROM A
WHERE posted_date >= '2015-07-27 00:00:00.000'
AND posted_date <= '2015-07-27 23:59:59.997' --Round down and keep equality
-- Option 3
SELECT
*
FROM A
WHERE CAST(posted_date AS DATE) = '2015-07-27' -- Use different data type
-- Option 4
SELECT
*
FROM A
WHERE CONVERT(CHAR(8), DateColumn, 112) = '20150727' -- Cast to string stripping off time
-- Option 5
SELECT
*
FROM A
WHERE posted_date BETWEEN '2015-07-27 00:00:00.000'
AND '2015-07-27 23:59:59.997' --Use between
Trong số năm tùy chọn tôi đã trình bày ở trên, tôi sẽ xem xét các lựa chọn 1 và 3 là các tùy chọn khả thi duy nhất. Chúng truyền đạt ý định của bạn một cách rõ ràng và sẽ không bị phá vỡ nếu bạn cập nhật các loại dữ liệu. Nếu bạn đang sử dụng SQL Server 2008 hoặc mới hơn, tôi nghĩ tùy chọn 3 nên là cách tiếp cận ưa thích của bạn. Điều đó đặc biệt đúng nếu bạn có thể thay đổi từ việc sử dụng DATETIMEkiểu dữ liệu thành kiểu DATEdữ liệu cho posted_date
cột của mình .
Liên quan đến phương án 3, một lời giải thích rất hay về một số vấn đề có thể được tìm thấy ở đây: Diễn viên cho đến nay là rất lớn nhưng đó có phải là một ý tưởng tốt?
Tôi không thích các tùy chọn 2 và 5 vì các .997
giây phân số sẽ chỉ là một số ma thuật khác mà mọi người sẽ muốn "sửa chữa". Đối với một số lý do tại sao BETWEEN
không được chấp nhận rộng rãi, bạn có thể muốn kiểm tra bài đăng này .
Tôi không thích tùy chọn 4 vì việc chuyển đổi các loại dữ liệu thành một chuỗi cho mục đích so sánh cảm thấy bẩn đối với tôi. Một lý do chất lượng hơn để tránh nó trong SQL Server là nó ảnh hưởng đến tính khả dụng hay còn gọi là bạn không thể thực hiện tìm kiếm chỉ mục và điều đó thường dẫn đến hiệu suất kém hơn.
Để biết thêm thông tin về cách đúng và sai cách xử lý các truy vấn phạm vi ngày, hãy kiểm tra bài đăng này của Aaron Bertrand .
Khi chia tay, bạn sẽ có thể giữ truy vấn ban đầu của mình và nó sẽ hoạt động như mong muốn nếu bạn thay đổi posted_date
cột của mình từ a DATETIMEsang a DATETIME2(3)
. Điều đó sẽ tiết kiệm không gian lưu trữ trên máy chủ, cung cấp cho bạn độ chính xác cao hơn với cùng độ chính xác, tuân thủ tiêu chuẩn / di động hơn và cho phép bạn dễ dàng điều chỉnh độ chính xác / độ chính xác nếu nhu cầu của bạn thay đổi trong tương lai. Tuy nhiên, đây chỉ là một tùy chọn nếu bạn đang sử dụng SQL Server 2008 hoặc mới hơn.
Như một câu đố nhỏ, 1/300
độ chính xác thứ hai DATETIMEdường như bị giữ lại từ UNIX theo câu trả lời StackOverflow này . Sybase có di sản chung có 1/300
độ chính xác thứ hai về kiểu dữ liệu DATETIME
vàTIME
dữ liệu của chúng nhưng các chữ số có ý nghĩa nhỏ nhất của chúng là một điểm khác nhau ở "0", "3" và "6". Theo tôi 1/300
, độ chính xác thứ hai và / hoặc 3,33ms là một quyết định kiến trúc không may vì khối 4 byte trong thời gian trong DATETIMEkiểu dữ liệu của SQL Server có thể dễ dàng hỗ trợ độ chính xác 1ms.
datetime3
với 70 (so với 7) chữ số chính xác được thêm vào? Thực hành tốt nhất là sử dụng một giá trị trong đó độ chính xác không thành vấn đề, tức là <bắt đầu của giây tiếp theo, phút, giờ hoặc ngày so với <= kết thúc của giây trước, phút, giờ hoặc ngày.
Chuyển đổi ngầm định
Tôi cho rằng kiểu dữ liệu post_date là Datetime. Tuy nhiên, không có vấn đề gì nếu loại ở phía bên kia là Datetime, Datetime2 hay chỉ là Time vì chuỗi (Varchar) sẽ được chuyển đổi hoàn toàn thành Datetime.
Với post_date được khai báo là Datetime2 (hoặc Time), posted_date <= '2015-07-27 23:59:59.99999'
mệnh đề where không thành công vì altough 23:59:59.99999
là giá trị Datetime2 hợp lệ, đây không phải là giá trị Datetime hợp lệ:
Conversion failed when converting date and/or time from character string.
Phạm vi thời gian cho ngày
Phạm vi thời gian của Datetime là 00:00:00 đến 23: 59: 59.997. Do đó, 23: 59: 59.999 nằm ngoài phạm vi và phải được làm tròn lên hoặc xuống đến giá trị gần nhất.
Độ chính xác
Bên cạnh các giá trị Datetime được làm tròn theo số gia của .000, .003 hoặc .007 giây. (ví dụ: 000, 003, 007, 010, 013, 017, 020, ..., 997)
Đây không phải là trường hợp với giá trị 2015-07-27 23:59:59.999
nằm trong phạm vi này: 2015-07-27 23:59:59.997
và 2015-07-28 0:00:00.000
.
Phạm vi này tương ứng với các tùy chọn trước và sau gần nhất, cả hai đều kết thúc bằng .000, .003 hoặc .007.
Làm tròn lên hay xuống ?
Vì nó gần với 2015-07-28 0:00:00.000
(+1 so với -2) hơn 2015-07-27 23:59:59.997
, nên chuỗi được làm tròn và trở thành giá trị Ngày tháng này : 2015-07-28 0:00:00.000
.
Với giới hạn trên như 2015-07-27 23:59:59.998
(hoặc .995, .996, .997, .998), nó sẽ được làm tròn xuống 2015-07-27 23:59:59.997
và truy vấn của bạn sẽ hoạt động như mong đợi. Tuy nhiên, nó không phải là một giải pháp mà chỉ là một giá trị may mắn.
Datetime2 hoặc các loại thời gian
Datetime2 và Thời gian thời gian dao động là 00:00:00.0000000
thông qua 23:59:59.9999999
với độ chính xác 100ns (chữ số cuối cùng khi được sử dụng với một độ chính xác 7 chữ số).
Tuy nhiên, phạm vi Datetime (3) không giống với phạm vi Datetime:
0:0:00.000
để23:59:59.997
0:0:00.000000000
để23:59:59.999
Dung dịch
Cuối cùng, an toàn hơn là tìm kiếm ngày bên dưới vào ngày hôm sau so với ngày bên dưới hoặc bằng với những gì bạn nghĩ đó là mảnh thời gian cuối cùng trong ngày. Điều này chủ yếu là vì bạn biết rằng ngày hôm sau luôn bắt đầu từ 0: 00: 00.000 nhưng các loại dữ liệu khác nhau có thể không có cùng thời gian vào cuối ngày:
Datetime `0:0:00.000` to `23:59:59.997`
Datetime2 `0:0:00.000000000` to `23:59:59.999-999-900`
Time2 `0:0:00.000000000` to `23:59:59.999-999-900`
< 2015-07-28 0:00:00.000
sẽ cho bạn một kết quả chính xác và là lựa chọn tốt nhất<= 2015-07-27 23:59:59.xxx
có thể trả về các giá trị bất ngờ khi nó không được làm tròn đến mức bạn nghĩ nó phải như vậy.Chúng tôi có thể nghĩ rằng việc thay đổi [post_date] thành Datetime2 và độ chính xác cao hơn của nó có thể khắc phục vấn đề này nhưng nó sẽ không giúp ích gì vì chuỗi vẫn được chuyển đổi thành Datetime. Tuy nhiên, nếu một diễn viên được thêm vào cast(2015-07-27 23:59:59.999' as datetime2)
, điều này hoạt động tốt
Diễn viên và chuyển đổi
Truyền có thể chuyển đổi một giá trị có tối đa 3 chữ số thành Datetime hoặc tối đa 9 chữ số thành Datetime2 hoặc Time và làm tròn nó với độ chính xác chính xác.
Cần lưu ý rằng Cast of Datetime2 và Time2 có thể cho các kết quả khác nhau:
select cast('20150101 23:59:59.999999999' as datetime2(7))
được làm tròn 2015-05-03 00: 00: 00.0000000 (đối với giá trị lớn hơn 999999949)select cast('23:59:59.999999999' as time(7))
=> 23: 59: 59.9999999Cách khắc phục sự cố mà datetime đang gặp phải với các mức tăng 0, 3 và 7 mặc dù vẫn luôn tốt hơn để tìm ngày trước nano thứ 1 của ngày tiếp theo (luôn là 0: 00: 00.000).
Nguồn MSDN: datetime (Transact-SQL)
Nó đang làm tròn
select cast('2015-07-27 23:59:59.999' as datetime)
returns 2015-07-28 00:00:00.000
.998, .997, .996, .995 tất cả các diễn viên / vòng đến .997
Nên dùng
select *
from A
where posted_date >= '2015-07-27 00:00:00.000'
and posted_date < '2015-07-28 00:00:00.000'
hoặc là
where cast(posted_date as date) = '2015-07-27'
Xem độ chính xác trong liên kết này
Luôn được báo cáo là .000, .003, .007
select * from A where date(posted_date) = '2015-07-27'
'DATE' is not a recognized built-in function name.
gives you control of DATE and TIME as opposed to datetime.
điều đó nghĩa là gì?