SQL giữa không bao gồm SQL


136

Tôi có một truy vấn như thế này:

SELECT * FROM Cases WHERE created_at BETWEEN '2013-05-01' AND '2013-05-01'

Nhưng điều này không mang lại kết quả nào mặc dù có dữ liệu về ngày 1.

created_atCó vẻ như 2013-05-01 22:25:19, tôi nghi ngờ nó có liên quan đến thời gian? Làm thế nào điều này có thể được giải quyết?

Nó chỉ hoạt động tốt nếu tôi thực hiện phạm vi ngày lớn hơn, nhưng nó cũng sẽ (bao gồm) hoạt động với một ngày duy nhất.


23
Chà, có bao nhiêu số nằm trong khoảng từ 1 đến 1? 1,5 nên là giữa 1 và 1? Chỉ không sử dụng GIỮA cho phạm vi ngày / giờ. Không bao giờ. Và hãy cẩn thận với cách bạn đánh giá "hoạt động tốt" - bạn đã kiểm tra chặt chẽ kết quả từ ngày cuối cùng trong phạm vi chưa? Bạn sẽ chỉ bao gồm tất cả các hàng nếu chúng không có thời gian liên quan đến chúng.
Aaron Bertrand

URL được cập nhật cho Aaron: sqlblog.org/2011/10/19/ Kẻ
JayRizzo

Câu trả lời:


294

bao gồm. Bạn đang so sánh datetimes với ngày. Ngày thứ hai được hiểu là nửa đêm khi ngày bắt đầu .

Một cách để khắc phục điều này là:

SELECT *
FROM Cases
WHERE cast(created_at as date) BETWEEN '2013-05-01' AND '2013-05-01'

Một cách khác để khắc phục nó là so sánh nhị phân rõ ràng

SELECT *
FROM Cases
WHERE created_at >= '2013-05-01' AND created_at < '2013-05-02'

Aaron Bertrand có một mục blog dài vào các ngày ( ở đây ), nơi ông thảo luận về vấn đề này và các vấn đề ngày khác.


15
Để hoàn thành vì câu trả lời hay này, tôi sẽ đề nghị sử dụng dateaddđể có được vào ngày hôm sau.
Tim Lehner

7
@TimLehner Để có câu trả lời hay, tôi đã sử dụng định dạng ISO-8601 là '20130501'. Đối với những người không phải là người Mỹ có dateformat dmy, bạn sẽ nhận được điều này:set dateformat dmy;select month(cast('2013-05-01' as datetime)); =1
RichardTheKiwi

2
@RichardTheKiwi - Tôi tin rằng bit rõ ràng đang sử dụng một khoảng thời gian đóng ở một đầu ( >=) và mở ở đầu kia ( <) kết hợp với việc kiểm tra một ngày sau ngày kết thúc đã chỉ định.
HABO

3
@scottb Cá nhân, tôi sẽ thấy khó chịu khi phải chuyển đổi sang datetimes mỗi lần tôi muốn hiển thị, xuất, nhập hoặc viết một lớp với datetime. Tôi nghĩ rằng SQL Server có rất nhiều chức năng tích hợp để thao tác và so sánh thời gian dữ liệu cho hầu hết các mục đích.
Tim Lehner

10
Bạn không bao giờ nên bỏ một cột trong wheremệnh đề, vì nó sẽ mất bất kỳ chỉ mục nào nó có. Đó là một mô hình thực sự xấu.
Buzinas

49

Nó đã được giả định rằng tham chiếu ngày thứ hai trong BETWEENcú pháp được coi là kỳ diệu là "cuối ngày" nhưng điều này là không đúng sự thật .

tức là điều này đã được mong đợi:

CHỌN * TỪ trường hợp 
WHERE created_at GIỮA bắt đầu '2013-05-01' VÀ kết thúc '2013-05-01'

Nhưng những gì thực sự xảy ra là thế này:

CHỌN * TỪ trường hợp 
WHERE created_at GIỮA '2013-05-01 00: 00: 00 + 00000 ' VÀ '2013-05-01 00: 00: 00 + 00000 '

Mà trở thành tương đương với:

CHỌN * TỪ các trường hợp WHERE created_at = '2013-05-01 00: 00: 00 + 00000 '

Vấn đề là một trong những nhận thức / kỳ vọng về BETWEENkhông bao gồm cả giá trị thấp hơn và các giá trị trên trong phạm vi, nhưng không kỳ diệu làm cho một ngày "bắt đầu" hoặc "kết thúc".

BETWEEN nên tránh khi lọc theo phạm vi ngày.

Luôn luôn sử dụng >= AND <thay thế

CHỌN * TỪ trường hợp 
WHERE (created_at > = '20130501'  created_at < '20130502')

dấu ngoặc đơn là tùy chọn ở đây nhưng có thể quan trọng trong các truy vấn phức tạp hơn.


2
Không chắc chắn về sự khôn ngoan của việc đăng bài này tốt sau khi câu hỏi đã được trả lời nhưng tôi muốn nhấn mạnh một điểm hơi khác
Được sử dụng_By_Al sẵn

5
Để biên tập viên; vui lòng không thử biến SQL giả thành các khối mã, đơn giản là nó không hoạt động vì nhận xét dựa trên BÓNG.
Được sử dụng_By_Al đã sẵn sàng vào

2
Để biên tập viên (một lần nữa) xin vui lòng, không thêm dấu ngoặc kép thông qua mã giả; họ KHÔNG giúp hiểu
Được sử dụng_By_Al sẵn

18

Bạn cần thực hiện một trong hai tùy chọn sau:

  1. Bao gồm thành phần thời gian trong betweenđiều kiện của bạn : ... where created_at between '2013-05-01 00:00:00' and '2013-05-01 23:59:59'(không được đề xuất ... xem đoạn cuối)
  2. Sử dụng bất đẳng thức thay vì between. Lưu ý rằng sau đó bạn sẽ phải thêm một ngày vào giá trị thứ hai:... where (created_at >= '2013-05-01' and created_at < '2013-05-02')

Sở thích cá nhân của tôi là lựa chọn thứ hai. Ngoài ra, Aaron Bertrandmột lời giải thích rất rõ ràng về lý do tại sao nó nên được sử dụng.


6
+1 cho 2. Nhưng -1 cho 1. Việc hack cuối ngày này hoàn toàn không đáng tin cậy và là một ý tưởng tồi.
Aaron Bertrand

1
@AaronBertrand Tôi cũng thích tùy chọn 2 (Tôi sử dụng nó thường xuyên). Nhưng tại sao bạn lại nói phương án 1 là "hoàn toàn không đáng tin cậy"?
Barranka

5
xin vui lòng đọc điều này đầy đủ . Bạn không bao giờ nên sử dụng BETWEENcho các truy vấn phạm vi ngày bao gồm thời gian; quá nhiều có thể đi sai
Aaron Bertrand


7

Tôi thấy rằng giải pháp tốt nhất để so sánh trường datetime với trường ngày là như sau:

DECLARE @StartDate DATE = '5/1/2013', 
        @EndDate   DATE = '5/1/2013' 

SELECT * 
FROM   cases 
WHERE  Datediff(day, created_at, @StartDate) <= 0 
       AND Datediff(day, created_at, @EndDate) >= 0 

Điều này tương đương với một bao gồm giữa các tuyên bố vì nó bao gồm cả ngày bắt đầu và ngày kết thúc cũng như những ngày nằm giữa.


3
cast(created_at as date)

Điều đó sẽ chỉ hoạt động trong năm 2008 và các phiên bản mới hơn của SQL Server

Nếu bạn đang sử dụng phiên bản cũ hơn thì hãy sử dụng

convert(varchar, created_at, 101)

2
convert(varchar, created_at, 101)kết quả là như thế dd/MM/yyyyvà chuỗi của OP là yyyy-MM-dd, tôi nghĩ câu trả lời này sẽ không hoạt động;).
shA.t

2

Bạn có thể sử dụng date()hàm sẽ trích xuất ngày từ datetime và cung cấp cho bạn kết quả là ngày bao gồm:

SELECT * FROM Cases WHERE date(created_at)='2013-05-01' AND '2013-05-01'

0

Ngày Dyamic GIỮA truy vấn sql

var startDate = '2019-08-22';
var Enddate = '2019-10-22'
     let sql = "SELECT * FROM Cases WHERE created_at BETWEEN '?' AND '?'";
     const users = await mysql.query( sql, [startDate, Enddate]);
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.