MySQL-Zwischenklausel nicht inklusive?

142

Wenn ich eine Abfrage mit einer betweenKlausel ausführe , scheint sie den Endwert auszuschließen.
Beispielsweise:

select * from person where dob between '2011-01-01' and '2011-01-31'

Dies ergibt alle Ergebnisse dobvon '2011-01-01' bis '2011-01-30'; Überspringen von Datensätzen, wo dob'2011-01-31' ist. Kann jemand erklären, warum sich diese Abfrage so verhält und wie ich sie so ändern kann, dass sie Datensätze enthält, in denen dob'2011-01-31' steht? (ohne 1 zum Enddatum hinzuzufügen, da es von den Benutzern ausgewählt wurde.)

ASD
quelle
Nee. Ich meine MySQL-Installation (Version?) BETWEENIst für beide Werte inklusive. Ich habe MySQL Server 5.7auf Windows 10.
Grün

Antworten:

181

Das Feld hat dobwahrscheinlich eine Zeitkomponente.

Um es abzuschneiden:

select * from person 
where CAST(dob AS DATE) between '2011-01-01' and '2011-01-31'
tiago2014
quelle
59
Stattdessen CAST(dob AS DATE)können Sie die prägnantere verwenden DATE(dob).
jkndrkn
11
Während dies funktioniert, erhalten Sie eine bessere Leistung, wenn Sie >=und <anstelle von verwenden between.
David Harkness
112
Mit erhalten Sie eine bessere Leistung dob BETWEEN '2011-01-01 00:00:00' AND '2011-01-31 23:59:59. Dies liegt daran DATE(dob), dass für jede Zeile ein Wert berechnet werden muss und keine Indizes für dieses Feld verwendet werden können.
Joshuahedlund
2
@joshuahedlund Bitte fügen Sie eine Antwort mit dieser Lösung hinzu. CAST ist nicht annähernd so effizient.
doc_id
3
@joshuahedlund Das funktioniert so lange, bis Sie Daten mit Zeiten haben t > 23:59:59 and t < 24:00:00. Warum überhaupt mit schlecht spezifizierten umgehen BETWEEN? Befolgen Sie lieber Davids Rat und Gebrauch : WHERE dob >= '2011-01-01' AND dob < '2011-02-01'. Beste Leistung, und es funktioniert jedes Mal.
Desillusioniert
300

Aus dem MySQL-Handbuch :

Dies entspricht dem Ausdruck (min <= Ausdruck UND Ausdruck <= max)

Frank Heikens
quelle
3
Das in dieser Antwort verknüpfte Handbuch zeigt, dass beim Vergleichen von DATE- und DATETIME-Objekten eine Besetzung bevorzugt wird. Ich denke, @tiagoinu hat die vollständigste Antwort im strengsten Sinne, aber beide sind genau richtig.
Kingsolmn
@ Jemminger kann sein, weil die Antwort von Erzrivalen- Postgres-Typ ist: P
Nawfal
27
Kurz gesagt, dazwischen ist inklusive ... deshalb rockt diese Antwort.
Rafael
6
Alter Kommentar, aber ich wollte dies mit der spezifischen Abfrage verknüpfen. "ZWISCHEN" ist inklusive, datiert jedoch ohne Zeitangabe auf 00:00:00. Der Vergleich eines Datumsbereichs verliert daher den letzten Tag. Rufen Sie entweder DATE (dob) auf oder geben Sie das Ende des Tages an.
wintermute92
Sie sagen, Übung ist Gold, aus meinem Anwendungsfall ist sie überhaupt nicht inklusive. Ich frage mich, warum dies bei mir passiert. Ich habe es versucht und manchmal funktioniert es manchmal nicht. Verwenden Sie es im TIME-Datenfeld.
Jeffery ThaGintoki
99

Das Problem ist, dass der 31.01.2011 wirklich der 31.01.2011 00:00:00 ist. Das ist der Anfang des Tages. Alles während des Tages ist nicht inbegriffen.

Daniel Hilgarth
quelle
19
Dies erklärt wirklich, was los ist und beantwortet die Frage.
Ivan P
3
Nach all den Jahren ist diese Antwort immer noch die beste. Vielen Dank.
Strabek
30
select * from person where dob between '2011-01-01 00:00:00' and '2011-01-31 23:59:59'
Gaurav
quelle
1
Ich denke, es ist erwähnenswert, dass dies keine Daten zu, 2011-01-31 23:59:59aber diejenigen bis zur 2011-01-31 23:59:58 letzten Sekunde des Tages enthält. Es könnte geringfügig sein, aber jemand wird davon profitieren.
doc_id
1
rahmanisback aus der MySQL-Dokumentation Ich kann bestätigen, dass die letzte Sekunde enthalten sein wird, da ZWISCHEN in beide Richtungen inklusive ist. siehe dev.mysql.com/doc/refman/5.5/en/...
Felype
1
Ja, @Felype, du hast recht. Ich habe dies selbst in der MySQL-Datenbank überprüft. Es enthält auch das 23:59:59im Ergebnis. Es ist also beides inklusive.
Glücklicher
2
Wenn es sich bei der dobSpalte um einen Zeitstempel mit einer Genauigkeit von weniger als einer Sekunde handelt, werden BETWEENEreignisse innerhalb der letzten Sekunde des Tages nicht verpasst, es sei denn, stattdessen wird "2011-02-01 00:00:00" verwendet.
Stickstoff
1
-1. Wird nicht enthalten 2011-01-31 23:59:59.003. @nitrogen using 2011-02-01 000:00:00wird am 1. Februar fälschlicherweise die Nullzeit einschließen .... Aus diesem Grund >=und <sollte stattdessen verwendet werden.
Desillusioniert
6

Ist das Feld , das Sie verweisen in Ihrer Anfrage ein Datum Typ oder ein Datetime - Typ?

Eine häufige Ursache für das von Ihnen beschriebene Verhalten ist die Verwendung eines DateTime-Typs, bei dem Sie eigentlich einen Date-Typ verwenden sollten. Das heißt, es sei denn, Sie müssen wirklich wissen, wann jemand geboren wurde, verwenden Sie einfach den Datumstyp.

Der Grund, warum der letzte Tag nicht in Ihren Ergebnissen enthalten ist, ist die Art und Weise, in der die Abfrage den Zeitanteil der Daten annimmt, die Sie in Ihrer Abfrage nicht angegeben haben.

Das heißt: Ihre Abfrage wird zwischen dem 30.01.2011 und dem 31.01.2011 als bis Mitternacht interpretiert, aber die Daten können später am Tag des 31.01.2011 einen Wert haben.

Vorschlag: Ändern Sie das Feld in den Datumstyp, wenn es sich um einen DateTime-Typ handelt.

JohnFx
quelle
4

Hallo, diese Abfrage funktioniert bei mir.

select * from person where dob between '2011-01-01' and '2011-01-31 23:59:59'
infinito84
quelle
2
select * from person where DATE(dob) between '2011-01-01' and '2011-01-31'

Überraschenderweise sind solche Konvertierungen Lösungen für viele Probleme in MySQL.

betty.88
quelle
10
Überraschenderweise ist dies genau das, was die akzeptierte Antwort (und mehrere andere) gesagt haben ... 2 Jahre bevor Sie es getan haben.
Chris Baker
0

Stellen Sie das obere Datum auf Datum + 1 Tag ein. Stellen Sie es in Ihrem Fall auf 2011-02-01 ein.

Rafal
quelle
1
Dies schließt fälschlicherweise die Nullzeit am 1. Februar ein. Aus diesem Grund BETWEENsollte dies ignoriert werden. aber >=und <sollte stattdessen verwendet werden.
Desillusioniert
0

Sie können die Abfrage wie folgt ausführen:

select * from person where dob between '2011-01-01' and '2011-01-31 23:59:59'

wie andere darauf hingewiesen haben, wenn Ihre Daten fest codiert sind.

Wenn sich das Datum in einer anderen Tabelle befindet, können Sie einen Tag hinzufügen und eine Sekunde subtrahieren (wenn die Daten ohne Sekunde / Uhrzeit gespeichert werden), z.

select * from person JOIN some_table ... where dob between some_table.initial_date and (some_table.final_date + INTERVAL 1 DAY - INTERVAL 1 SECOND)

Vermeiden Sie Casts auf den dobFeldern (wie in der akzeptierten Antwort), da dies zu großen Leistungsproblemen führen kann (z. B. wenn Sie keinen Index im dobFeld verwenden können, vorausgesetzt, es gibt einen). Der Ausführungsplan kann sich von using index conditionzu ändern , using wherewenn Sie so etwas wie DATE(dob)oder machen. Seien Sie CAST(dob AS DATE)also vorsichtig!

Lucas Basquerotto
quelle
0

In MySql sind die Werte inklusive, wenn Sie versuchen, zwischen '2011-01-01' und '2011-01-31' zu wechseln.

es wird von 2011-01-01 00:00:00bis zu 2011-01-31 00:00:00 daher eigentlich nichts in 2011-01-31 enthalten, da seine Zeit ab gehen sollte2011-01-31 00:00:00 ~ 2011-01-31 23:59:59

Für die Obergrenze, die Sie ändern können, 2011-02-01werden alle Daten bis zu abgerufen2011-01-31 23:59:59

Ambleu
quelle