SQL "zwischen" nicht inklusive

136

Ich habe eine Frage wie diese:

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

Dies führt jedoch zu keinen Ergebnissen, obwohl Daten zum 1. vorliegen.

created_atsieht aus wie 2013-05-01 22:25:19, ich vermute es hat mit der zeit zu tun? Wie könnte dies gelöst werden?

Es funktioniert gut, wenn ich größere Datumsbereiche mache, aber es sollte (einschließlich) auch mit einem einzigen Datum funktionieren.

JBurace
quelle
23
Nun, wie viele Zahlen liegen zwischen 1 und 1? Sollte 1,5 zwischen 1 und 1 liegen? Verwenden Sie ZWISCHEN nur nicht für Datums- / Zeitbereiche. Je. Und seien Sie vorsichtig, wie Sie "funktioniert einwandfrei" bewerten - haben Sie die Ergebnisse vom letzten Tag im Sortiment genau untersucht? Sie würden nur alle Zeilen einschließen, wenn ihnen keine Zeit zugeordnet wäre.
Aaron Bertrand
Aktualisierte URL für Aaron: sqlblog.org/2011/10/19/…
JayRizzo

Antworten:

294

Es ist inklusive. Sie vergleichen Datums- und Uhrzeitangaben mit Datumsangaben. Das zweite Datum wird als Mitternacht interpretiert, wenn der Tag beginnt .

Eine Möglichkeit, dies zu beheben, ist:

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

Eine andere Möglichkeit, dies zu beheben, sind explizite binäre Vergleiche

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

Aaron Bertrand hat einen langen Blogeintrag zu Daten ( hier ), in dem er dieses und andere Datumsfragen bespricht.

Gordon Linoff
quelle
15
Der Vollständigkeit halber würde ich für diese gute Antwort empfehlen, sie dateaddfür den nächsten Tag zu verwenden.
Tim Lehner
7
@ TimLehner Für eine gute Antwort hätte ich das ISO-8601-Format '20130501' verwendet. Für Nicht-US-Leute mit Datumsformat dmy erhalten Sie Folgendes:set dateformat dmy;select month(cast('2013-05-01' as datetime)); =1
RichardTheKiwi
2
@RichardTheKiwi - Ich glaube, das explizite Bit verwendet ein Intervall, das an einem Ende ( >=) geschlossen und am anderen ( <) geöffnet ist, kombiniert mit der Überprüfung eines Tages nach dem angegebenen Enddatum.
HABO
3
@scottb Persönlich würde ich es ärgerlich finden, jedes Mal, wenn ich eine Klasse mit einer Datumszeit anzeigen, exportieren, importieren oder schreiben wollte, in Datumsangaben konvertieren zu müssen. Ich denke, SQL Server verfügt über zahlreiche integrierte Funktionen zum Bearbeiten und Vergleichen von Datenzeiten für die meisten Zwecke.
Tim Lehner
10
Sie sollten niemals eine Spalte in die whereKlausel umwandeln, da dadurch die Indizierung verloren geht. Es ist ein wirklich schlechtes Muster.
Buzinas
49

Es wurde angenommen, dass die zweite Datumsreferenz in der BETWEENSyntax auf magische Weise als "Ende des Tages" betrachtet wird, aber dies ist nicht wahr .

dh dies wurde erwartet:

SELECT * FROM Cases 
WHERE created_at ZWISCHEN dem Beginn von '2013-05-01' UND dem Ende von '2013-05-01'

aber was wirklich passiert ist folgendes:

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

Welches wird das Äquivalent von:

SELECT * FROM Fälle WHERE created_at = '2013-05-01 00: 00: 00 + 00000 '

Das Problem ist einer der Wahrnehmungen / Erwartungen an BETWEENdenen sie schließen sowohl den unteren Wert und die oberen Werte im Bereich, aber nicht auf magische Weise ein Datum „Anfang von“ oder „Ende“ machen.

BETWEEN sollte beim Filtern nach Datumsbereichen vermieden werden.

Verwenden Sie >= AND <stattdessen immer die

SELECT * FROM Cases 
WHERE (created_at > = '20130501' UND created_at < '20130502')

Die Klammern sind hier optional, können jedoch bei komplexeren Abfragen wichtig sein.

Used_By_Already
quelle
2
Unsicher über die Weisheit, dies gut zu
posten,
5
An die Redakteure; Bitte versuchen Sie nicht, das Pseudo-SQL in Codeblöcke zu verwandeln. Es funktioniert einfach nicht, da der Kommentar auf BOLD basiert.
Used_By_Already
2
Fügen Sie den Redakteuren (erneut) keine falschen Anführungszeichen durch den Pseudocode hinzu. Sie helfen NICHT beim Verständnis.
Used_By_Already
18

Sie müssen eine dieser beiden Optionen ausführen:

  1. Nehmen Sie die Zeitkomponente in Ihren betweenZustand auf: ... where created_at between '2013-05-01 00:00:00' and '2013-05-01 23:59:59'(nicht empfohlen ... siehe letzter Absatz)
  2. Verwenden Sie Ungleichungen anstelle von between. Beachten Sie, dass Sie dann einen Tag zum zweiten Wert hinzufügen müssen:... where (created_at >= '2013-05-01' and created_at < '2013-05-02')

Meine persönliche Präferenz ist die zweite Option. Auch Aaron Bertrand hat eine sehr klare Erklärung, warum es verwendet werden soll.

Barranka
quelle
6
+1 für 2. Aber -1 für 1. Dieser Hack am Ende des Tages ist völlig unzuverlässig und eine schlechte Idee.
Aaron Bertrand
1
@ AaronBertrand Ich bevorzuge auch Option 2 (ich benutze es häufig). Aber warum ist Option 1 Ihrer Meinung nach "völlig unzuverlässig"?
Barranka
5
Bitte lesen Sie dies vollständig . Sie sollten niemals BETWEENfür Datumsbereichsabfragen verwenden, die die Zeit enthalten. viel zu viel kann schief gehen.
Aaron Bertrand
7

Verwenden Sie einfach den Zeitstempel als Datum:

SELECT * FROM Cases WHERE date(created_at)='2013-05-01' 
Balinti
quelle
4
Die Frage ist mit SQL Server gekennzeichnet und 'DATE' is not a recognized built-in function name.muss stackoverflow.com/a/20973452/4233593
Jeff Puckett
7

Ich finde, dass die beste Lösung zum Vergleichen eines Datums- / Uhrzeitfelds mit einem Datumsfeld die folgende ist:

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 

Dies entspricht einer Inklusiv-Zwischen-Anweisung, da sie sowohl das Start- und Enddatum als auch diejenigen enthält, die dazwischen liegen.

Ted
quelle
3
cast(created_at as date)

Dies funktioniert nur in 2008 und neueren Versionen von SQL Server

Wenn Sie eine ältere Version verwenden, verwenden Sie

convert(varchar, created_at, 101)
Ali Adravi
quelle
2
convert(varchar, created_at, 101)Ergebnis ist wie dd/MM/yyyyund OPs Zeichenfolgen sind yyyy-MM-dd, ich denke, diese Antwort wird nicht funktionieren;).
shA.t
2

Sie können die date()Funktion verwenden, mit der das Datum aus einer Datums- / Uhrzeitangabe extrahiert und das Ergebnis als Inklusivdatum angegeben wird:

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

Dyamic Datum zwischen SQL-Abfrage

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.
quelle