包括的ではない「間」のSQL


136

次のようなクエリがあります。

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

ただし、1日のデータがあっても結果は得られません。

created_atのよう2013-05-01 22:25:19に見えます、それは時間と関係があるのではないでしょうか?これをどのように解決できますか?

日付範囲を大きくすると問題なく機能しますが、単一の日付でも(包括的に)機能するはずです。


23
さて、1と1の間の数はいくつですか?1.5は1と1の間にあるべきですか?日付/時刻の範囲にBETWEENを使用しないでください。ずっと。また、「正常に機能する」ことを評価する方法に注意してください。範囲の最終日の結果を綿密に調べましたか?行が関連付けられていない場合にのみ、すべての行を含めます。
Aaron Bertrand

アーロンの更新されたURL:sqlblog.org/2011/10/19/...
JayRizzo

回答:


296

それ包括的です。日時と日付を比較しています。2番目の日付は、その日が始まる真夜中と解釈されます

これを修正する1つの方法は次のとおりです。

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

それを修正する別の方法は、明示的なバイナリ比較です

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

Aaron Bertrandは日付に関する長いブログエントリ(ここ)を持っています。そこで彼はこれと他の日付の問題について議論しています。


15
この良い答えを完全にするために、私はを使っdateaddて翌日を取得することをお勧めします。
Tim Lehner、2013年

7
@TimLehner良い答えのために、私は '20130501'のISO-8601形式を使用したでしょう。DATEFORMAT DMYと米国以外の人々のために、あなたはこの取得:set dateformat dmy;select month(cast('2013-05-01' as datetime)); =1
RichardTheKiwi

2
@RichardTheKiwi- 明示的なビットは、指定された終了日の1日後のチェックと組み合わせて、一方の端(>=)で閉じ、もう一方の端()で開く間隔を使用していると思い<ます。
HABO 2013年

3
@scottb個人的には、日時を持つクラスを表示、エクスポート、インポート、または書き込みするたびに、日時に変換する必要があるのは面倒です。SQL Serverには、ほとんどの目的で日時を操作および比較するための組み込み機能がたくさんあると思います。
Tim Lehner、2013年

10
where句の列はキャストしないでください。列のインデックスが失われるためです。それは本当に悪いパターンです。
ブジナス

49

BETWEEN構文の2番目の日付参照は、魔法のように「1日の終わり」と見なされると想定されていましたが、これは正しくありません。

つまり、これは予想されたものです。

SELECT * FROMケース 
WHERE created_at '2013-05-01'の始まりの終わりの間

しかし、実際に何が起こるのですか?

SELECT * FROMケース 
WHERE created_at BETWEEN '2013-05-01 00:00:00 + 00000 ' AND '2013-05-01 00:00:00 + 00000 '

これは以下と同等になります:

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

問題は、約認識/期待の一つであるBETWEENない低い値および範囲の上側の値との両方を含むが、これらありません魔法の日がか「の終わり」「の始まり」を作ります。

BETWEEN 日付範囲でフィルタリングする場合は避けてください。

常に使用する>= AND <代わりに

SELECT * FROMケース 
WHERE(のcreated_at > = '20130501' のcreated_at < '20130502')

括弧はここではオプションですが、より複雑なクエリでは重要になる場合があります。


2
質問への回答が終わった後、これを投稿することの賢明さについては不明ですが、少し異なる点を強調したいと思います
Used_By_Already

5
編集者へ。疑似SQLをコードブロックにしようとしないでください。コメントがBOLDに依存しているため、機能しません。
Used_By_Already 2014年

2
編集者(再度)に、疑似コードを介して偽の引用符を追加しないでください。理解を助けるものではありません。
Used_By_Already

18

次の2つのオプションのいずれかを実行する必要があります。

  1. 時間コンポーネントをbetween条件に含める:(... where created_at between '2013-05-01 00:00:00' and '2013-05-01 23:59:59'非推奨...最後の段落を参照)
  2. の代わりに不等式を使用しbetweenます。次に、2番目の値に1日を追加する必要があることに注意してください。... where (created_at >= '2013-05-01' and created_at < '2013-05-02')

私の個人的な好みは2番目のオプションです。また、Aaron Bertrand、なぜそれを使用すべきかについて非常に明確な説明をしています。


6
2の+1。ただし1の-1。この1日の終わりのハックは完全に信頼できず、悪い考えです。
アーロンベルトラン

1
@AaronBertrand私はオプション2も好みます(私は頻繁に使用しています)。しかし、なぜオプション1は「完全に信頼できない」と言うのでしょうか。
Barranka 2013年

5
これを完全に読んでくださいBETWEEN時間を含む日付範囲クエリには使用しないでください。方法が多すぎるとうまくいかない場合があります。
アーロンバートランド


7

日時フィールドを日付フィールドと比較するための最良の解決策は次のとおりです。

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 

これは、開始日と終了日、およびその間に含まれる日付の両方を含むため、包括的なbetweenステートメントと同等です。


3
cast(created_at as date)

これは、SQL Serverの2008以降のバージョンでのみ機能します

古いバージョンを使用している場合は、

convert(varchar, created_at, 101)

2
convert(varchar, created_at, 101)結果は次のようにdd/MM/yyyyなり、OPの文字列はyyyy-MM-ddです。この答えは機能しないと思います;)
shA.t 2015

2

date()日時から日付を抽出し、結果を包括的日付として返す関数を使用できます。

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

0

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]);
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.