SQL "antara" tidak termasuk

136

Saya punya pertanyaan seperti ini:

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

Tapi ini tidak memberikan hasil meskipun ada data pada tanggal 1.

created_atSepertinya 2013-05-01 22:25:19, saya kira itu ada hubungannya dengan waktu? Bagaimana ini bisa diselesaikan?

Ini berfungsi dengan baik jika saya melakukan rentang tanggal yang lebih besar, tetapi seharusnya (inklusif) bekerja dengan satu tanggal juga.

JBurace
sumber
23
Nah, berapa angka antara 1 dan 1? Haruskah 1,5 berada di antara 1 dan 1? Hanya saja, jangan gunakan ANTARA untuk rentang tanggal / waktu. Pernah. Dan berhati-hatilah bagaimana Anda menilai "bekerja dengan baik" - apakah Anda telah memeriksa dengan cermat hasil dari hari terakhir dalam jangkauan? Anda hanya akan memasukkan semua baris jika mereka tidak punya waktu terkait dengan mereka.
Aaron Bertrand
URL yang diperbarui untuk Aaron: sqlblog.org/2011/10/10/19/...
JayRizzo

Jawaban:

296

ini adalah inklusif. Anda membandingkan waktu data dengan tanggal. Tanggal kedua ditafsirkan sebagai tengah malam ketika hari dimulai .

Salah satu cara untuk memperbaikinya adalah:

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

Cara lain untuk memperbaikinya adalah dengan perbandingan biner eksplisit

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

Aaron Bertrand memiliki entri blog yang panjang pada tanggal (di sini ), di mana ia membahas ini dan masalah tanggal lainnya.

Gordon Linoff
sumber
15
Demi kelengkapan jawaban yang bagus ini, saya sarankan menggunakan dateadduntuk mendapatkan hari berikutnya.
Tim Lehner
7
@ TimLehner Demi jawaban yang bagus, saya akan menggunakan format ISO-8601 '20130501'. Untuk orang-orang non-AS dengan dateformat dmy, Anda mendapatkan ini:set dateformat dmy;select month(cast('2013-05-01' as datetime)); =1
RichardTheKiwi
2
@RichardTheKiwi - Saya percaya bit eksplisit menggunakan interval yang ditutup pada satu ujung ( >=) dan terbuka di ujung lainnya ( <) dikombinasikan dengan memeriksa satu hari di luar tanggal akhir yang ditentukan.
HABO
3
@scottb Secara pribadi, saya merasa menjengkelkan harus mengonversi ke datetimes setiap kali saya ingin menampilkan, mengekspor, mengimpor, atau menulis kelas dengan datetime. Saya pikir SQL Server memiliki banyak fungsi bawaan untuk memanipulasi dan membandingkan data untuk sebagian besar keperluan.
Tim Lehner
10
Anda seharusnya tidak pernah membuat kolom di whereklausa, karena akan kehilangan pengindeksan yang dimilikinya. Ini pola yang sangat buruk.
Buzinas
49

Diasumsikan bahwa referensi kencan kedua dalam BETWEENsintaks secara ajaib dianggap sebagai "akhir hari" tetapi ini tidak benar .

yaitu ini yang diharapkan:

PILIH * DARI KASUS 
WHERE create_at ANTARA awal '2013-05-01' DAN akhir '2013-05-01'

tetapi yang sebenarnya terjadi adalah ini:

PILIH * DARI KASUS 
WHERE create_at BETWEEN '2013-05-01  00: 00: 00 + 00000 ' AND '2013-05-01 00: 00: 00 + 00000 '

Yang menjadi setara dengan:

SELECT * FROM Cases WHERE create_at = '2013-05-01 00: 00: 00 + 00000 '

Masalahnya adalah salah satu persepsi / harapan tentang BETWEENyang tidak termasuk KEDUA nilai yang lebih rendah dan nilai-nilai atas dalam kisaran, tetapi tidak secara ajaib membuat kencan yang "awal" atau "akhir".

BETWEEN harus dihindari saat memfilter menurut rentang tanggal.

Selalu gunakan >= AND <saja

PILIH * DARI KASUS 
WHERE (Created_at > = '20130501' AND Created_at < '20130502')

tanda kurung opsional di sini tetapi bisa menjadi penting dalam pertanyaan yang lebih kompleks.

Used_By_Already
sumber
2
Tidak yakin tentang kebijaksanaan memposting ini dengan baik setelah pertanyaan telah dijawab, tetapi saya ingin menekankan poin yang sedikit berbeda
Used_By_Already
5
Kepada editor; tolong jangan mencoba membuat pseudo SQL menjadi blok kode, itu tidak berfungsi karena komentar bergantung pada BOLD.
Used_By_Already
2
Kepada editor (lagi) tolong, jangan menambahkan tanda kutip palsu melalui kode semu; mereka TIDAK membantu pemahaman.
Used_By_Already
18

Anda perlu melakukan salah satu dari dua opsi ini:

  1. Sertakan komponen waktu dalam betweenkondisi Anda : ... where created_at between '2013-05-01 00:00:00' and '2013-05-01 23:59:59'(tidak disarankan ... lihat paragraf terakhir)
  2. Gunakan ketidaksetaraan alih-alih between. Perhatikan bahwa Anda harus menambahkan satu hari ke nilai kedua:... where (created_at >= '2013-05-01' and created_at < '2013-05-02')

Preferensi pribadi saya adalah opsi kedua. Juga, Aaron Bertrand memiliki penjelasan yang sangat jelas tentang mengapa itu harus digunakan.

Barranka
sumber
6
+1 untuk 2. Tetapi -1 untuk 1. Retas akhir hari ini sama sekali tidak dapat diandalkan dan merupakan ide yang buruk.
Aaron Bertrand
1
@ AaronBertrand Saya juga lebih suka opsi 2 (saya sering menggunakannya). Tetapi mengapa Anda mengatakan opsi 1 "sama sekali tidak bisa diandalkan"?
Barranka
5
tolong baca ini sepenuhnya . Anda tidak boleh menggunakan BETWEENkueri rentang tanggal yang mencakup waktu; terlalu banyak yang bisa salah.
Aaron Bertrand
7

Cukup gunakan cap waktu sebagai tanggal:

SELECT * FROM Cases WHERE date(created_at)='2013-05-01' 
Balinti
sumber
4
Pertanyaan ini ditandai dengan SQL Server, dan 'DATE' is not a recognized built-in function name.Perlu mengonversi stackoverflow.com/a/20973452/4233593
Jeff Puckett
7

Saya menemukan bahwa solusi terbaik untuk membandingkan bidang datetime dengan bidang tanggal adalah sebagai berikut:

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 

Ini setara dengan inklusif antara pernyataan karena mencakup tanggal mulai dan berakhir serta yang termasuk di dalamnya.

Ted
sumber
3
cast(created_at as date)

Itu hanya akan berfungsi pada 2008 dan versi SQL Server yang lebih baru

Jika Anda menggunakan versi yang lebih lama maka gunakan

convert(varchar, created_at, 101)
Ali Adravi
sumber
2
convert(varchar, created_at, 101)hasilnya seperti dd/MM/yyyydan string OP adalah yyyy-MM-dd, saya pikir jawaban ini tidak akan berfungsi;).
shA.t
2

Anda dapat menggunakan date()fungsi yang akan mengekstrak tanggal dari suatu waktu dan memberi Anda hasilnya sebagai tanggal inklusif:

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

Tanggal dinamis ANTARA kueri 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]);
Muralidharan C
sumber