SQL “arasında” dahil değildir


136

Ben böyle bir sorgu var:

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

Ancak bu, 1'inde veriler olmasına rağmen sonuç vermiyor.

created_at2013-05-01 22:25:19zamanla ilgili olduğundan şüpheleniyorum gibi görünüyor ? Bu nasıl çözülebilir?

Daha büyük tarih aralıkları yaparsam iyi çalışır, ancak tek bir tarihle de (dahil) çalışması gerekir.


23
1 ile 1 arasında kaç sayı var? 1.5 1 ile 1 arasında mı olmalı? Tarih / saat aralıkları için BETWEEN işlevini kullanmayın. Hiç. Ve "gayet iyi çalışıyor" şeklini nasıl değerlendirdiğinize dikkat edin - aralıktaki son günden sonuçları yakından incelediniz mi? Tüm satırları yalnızca kendileriyle ilişkili zamanları yoksa eklersiniz.
Aaron Bertrand

Aaron için güncellenmiş URL: sqlblog.org/2011/10/19/…
JayRizzo

Yanıtlar:


296

O ise kapsayıcı. Tarihlerle tarihleri ​​karşılaştırıyorsunuz. İkinci tarih, günün başladığı gece yarısı olarak yorumlanır .

Bunu düzeltmenin bir yolu:

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

Bunu düzeltmenin başka bir yolu, açık ikili karşılaştırmalardır

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

Aaron Bertrand'ın ( burada ), bu ve diğer tarih konularını tartıştığı uzun bir blog girişi var .


15
Bütün bu iyi cevabın iyiliği dateaddiçin, ertesi günü almak için kullanmanızı öneririm .
Tim Lehner

7
@TimLehner İyi bir cevap uğruna, '20130501' ISO-8601 biçimini kullanırdım. Dateformat dmy'si olan ABD dışındaki insanlar için şunu elde edersiniz:set dateformat dmy;select month(cast('2013-05-01' as datetime)); =1
RichardTheKiwi

2
@RichardTheKiwi - Açık bitin bir uçta ( >=) kapalı ve diğer uçta ( ) <belirtilen bitiş tarihinin bir gün ötesinde kontrol edilmesiyle birlikte bir aralık kullandığına inanıyorum .
HABO

3
@scottb Kişisel olarak, bir tarih saatiyle bir sınıf görüntülemek, dışa aktarmak, içe aktarmak veya yazmak istediğim her seferinde günlüğe dönüşmek zorunda kalmayı sinir bozucu bulurdum. SQL Server, birçok amaç için tarihler arasında işlem yapmak ve karşılaştırmak için birçok yerleşik işlevselliğe sahip olduğunu düşünüyorum.
Tim Lehner

10
Sahip whereolduğu tüm dizinleri kaybedeceği için hiçbir zaman cümleye bir sütun dökmemelisiniz. Gerçekten kötü bir model.
Buzinas

49

BETWEENSözdizimindeki ikinci tarih referansının sihirli bir şekilde "günün sonu" olarak kabul edildiği varsayılmıştır, ancak bu doğru değildir .

yani bu beklenen bir durumdu:

Vakalardan SEÇ * 
NEREDE ARASINDAKİ created_at başında '2013-05-01' VE sonuna '2013-05-01'

ama asıl olan şu:

Vakalardan SEÇ * 
'2013-05-01 00: 00: 00 + 00000 ' VE '2013-05-01 00: 00: 00 + 00000 '
 ARASINDA NEREDE yaratıldı

Hangi eşdeğer olur:

SELECT * NEREDEN OLDUĞUNDA OLDUĞU OLAYLAR_t = '2013-05-01 00: 00: 00 + 00000 '

Sorun hakkında algıları / beklentilerin biridir BETWEENhangi gelmez düşük bir değer ve aralıktaki üst değerler İKİ içerir, ancak vermez sihirli bir tarih veya "sonuna" "başlangıcı" olun.

BETWEEN tarih aralıklarına göre filtrelerken kaçınılmalıdır.

Daima kullanmak >= AND <yerine

Vakalardan SEÇ * 
NEREDE (created_at > = '20130501' VE created_at < '20130502')

parantezler burada isteğe bağlıdır, ancak daha karmaşık sorgularda önemli olabilir.


2
Soru zaten cevaplandıktan sonra bunu iyi yayınlamanın bilgeliğinden emin değilim ama biraz farklı bir noktayı vurgulamak istedim
Used_By_Already 4:14

5
Editörlere; Lütfen sözde SQL'i kod blokları haline getirmeye çalışmayın, yorum BOLD'a bağlı olduğu için çalışmaz.
Used_By_Already

2
Editörlere (tekrar) lütfen sahte kod üzerinden sahte tırnak işaretleri eklemeyin; anlamalarına yardımcı DEĞİLDİR.
Used_By_Already

18

Bu iki seçenekten birini yapmanız gerekir:

  1. Zaman bileşenini betweenkoşulunuza ekleyin : ... where created_at between '2013-05-01 00:00:00' and '2013-05-01 23:59:59'(önerilmez ... son paragrafa bakın)
  2. Bunun yerine eşitsizlikleri kullanın between. O zaman ikinci değere bir gün eklemeniz gerekeceğine dikkat edin:... where (created_at >= '2013-05-01' and created_at < '2013-05-02')

Kişisel tercihim ikinci seçenek. Ayrıca, Aaron Bertrand vardır neden üzerinde çok net bir açıklama da kullanılmalıdır.


6
2 için +1. Ancak 1 için -1. Bu günün sonu kesmek tamamen güvenilmez ve kötü bir fikir.
Aaron Bertrand

1
@AaronBertrand Ayrıca 2. seçeneği tercih ederim (sık kullanıyorum). Peki neden 1. seçeneğin "tamamen güvenilmez" olduğunu söylüyorsunuz?
Barranka

5
lütfen bunu tam olarak okuyun . Asla BETWEENzaman içeren tarih aralığı sorguları için kullanmamalısınız ; çok fazla yanlış gidebilir.
Aaron Bertrand


7

Tarih alanı ile tarih alanını karşılaştırmanın en iyi çözümünün şu olduğunu düşünüyorum:

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 

Bu, hem başlangıç ​​hem de bitiş tarihinin yanı sıra aradaki ifadeleri de içerdiğinden, ifadeler arasındaki kapsayıcıya eşdeğerdir.


3
cast(created_at as date)

Bu sadece 2008 ve SQL Server'ın daha yeni sürümlerinde çalışacaktır

Eski sürümü kullanıyorsanız

convert(varchar, created_at, 101)

2
convert(varchar, created_at, 101)sonuç gibi dd/MM/yyyyve OP dizeleri, yyyy-MM-ddbu cevap işe yaramayacağını düşünüyorum;).
shA.t

2

date()Tarihi datetime'dan ayıracak ve sonucu kapsayıcı tarih olarak verecek işlevi kullanabilirsiniz :

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

0

Sql sorgusu arasındaki dinamik tarih

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]);
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.