Klauzula MySQL „between” nie obejmuje?


142

Jeśli uruchomię zapytanie z betweenklauzulą, wydaje się, że wyklucza wartość końcową.
Na przykład:

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

Spowoduje to wyświetlenie wszystkich wyników dobod „2011-01-01” do „2011-01-30”; pomijanie rekordów, gdzie dobjest „2011-01-31”. Czy ktoś może wyjaśnić, dlaczego to zapytanie zachowuje się w ten sposób i jak mogę je zmodyfikować, aby zawierało rekordy, w których dobjest „2011-01-31”? (bez dodawania 1 do daty końcowej, ponieważ została wybrana przez użytkowników.)


Nie. Moja instalacja MySQL (wersja?) BETWEENObejmuje obie wartości. Mam MySQL Server 5.7system Windows 10.
Zielony

Odpowiedzi:


181

Pole dobprawdopodobnie zawiera składnik czasu.

Aby go skrócić:

select * from person 
where CAST(dob AS DATE) between '2011-01-01' and '2011-01-31'

59
Zamiast CAST(dob AS DATE)ciebie możesz użyć bardziej zwięzłego DATE(dob).
jkndrkn

11
Chociaż to działa, lepszą wydajność uzyskasz, używając >=i <zamiast between.
David Harkness

112
Uzyskasz lepszą wydajność, używając dob BETWEEN '2011-01-01 00:00:00' AND '2011-01-31 23:59:59. Dzieje się tak, ponieważ DATE(dob)musi obliczyć wartość dla każdego wiersza i nie może używać żadnych indeksów w tym polu.
joshuahedlund

2
@joshuahedlund Dodaj odpowiedź za pomocą tego rozwiązania. CAST nie jest tak wydajne.
doc_id

3
@joshuahedlund Działa, dopóki nie masz danych z czasami t > 23:59:59 and t < 24:00:00. Po BETWEENco w ogóle zajmować się źle sprecyzowanymi ? Raczej postępować zgodnie ze wskazówkami i korzystanie Dawida: WHERE dob >= '2011-01-01' AND dob < '2011-02-01'. Najlepsza wydajność i działa za każdym razem.
Rozczarowany

300

Z podręcznika MySQL :

Jest to równoważne z wyrażeniem (min <= wyrażenie AND wyrażenie <= max)


3
Podręcznik powiązany w tej odpowiedzi pokazuje, że rzutowanie jest preferowane podczas porównywania obiektów DATE i DATETIME. Więc myślę, że @tiagoinu ma najbardziej kompletną odpowiedź w ścisłym tego słowa znaczeniu, ale obie są na miejscu.
Kingsolmn

@jemminger może być fakt, że odpowiedź jest od rywal -postgres facet: P
Nawfal

27
Krótko mówiąc, jest inkluzywna ... dlatego ta odpowiedź rządzi.
Rafael

6
Stary komentarz, ale chciałem powiązać to z konkretnym zapytaniem. „BETWEEN” jest wliczone w cenę, ale daty bez wpisu godziny do 00:00:00. Porównanie zakresu dat spowoduje zatem utratę ostatniego dnia. Zadzwoń pod numer DATE (numer dobowy) lub podaj koniec dnia.
wintermute92

mówią, że praktyka jest złotem, z mojego przypadku użycia w ogóle nie obejmuje, zastanawiam się, dlaczego tak się dzieje ze mną. Próbowałem i czasami to działa, czasami nie. używając go w polu danych CZAS.
Jeffery ThaGintoki,

99

Problem w tym, że 2011-01-31 tak naprawdę to 2011-01-31 00:00:00. To jest początek dnia. Nie obejmuje wszystkiego w ciągu dnia.


19
To naprawdę wyjaśnia, co się dzieje i odpowiada na pytanie.
Ivan P

3
Po tylu latach ta odpowiedź jest nadal najlepsza. Wielkie dzięki.
Strabek

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

1
Myślę , że warto zauważyć, że nie będzie to obejmowało dat o godzinie, 2011-01-31 23:59:59ale będzie obejmowało te do 2011-01-31 23:59:58 ostatniej sekundy dnia. Nie uwzględniono. Może to być niewielkie, ale ktoś na tym skorzysta.
doc_id

1
rahmanisback z MySQL Documentation Mogę potwierdzić, że ostatnia sekunda BĘDZIE uwzględniona, ponieważ BETWEEN obejmuje obie strony. zobacz dev.mysql.com/doc/refman/5.5/en/…
Felype

1
Tak, @Felype masz rację. Sam sprawdziłem to w bazie danych mysql. Obejmuje również 23:59:59wynik. Więc obie strony obejmują.
Lucky

2
Jeśli dobkolumna jest sygnaturą czasową z dokładnością do mniej niż sekundy, to BETWEENnadal nie będzie pomijać wydarzeń w ostatniej sekundzie dnia, chyba że zamiast tego zostanie użyty „2011-02-01 00:00:00”?
azot

1
-1. Nie obejmuje 2011-01-31 23:59:59.003. Używanie @nitrogen 2011-02-01 000:00:00będzie niepoprawnie obejmowało czas zerowy 1 lutego ... Dlatego >=i <powinno być używane zamiast tego.
Rozczarowany

6

Czy pole, do którego odwołujesz się w zapytaniu, jest typem daty czy typu daty i godziny ?

Typową przyczyną opisywanego zachowania jest użycie typu DateTime, w którym naprawdę należy używać typu Date. To znaczy, chyba że naprawdę potrzebujesz wiedzieć, kiedy ktoś się urodził, po prostu użyj typu Data.

Przyczyną, dla której ostatni dzień nie jest uwzględniany w wynikach, jest sposób, w jaki zapytanie przyjmuje część czasu z dat, których nie określono w zapytaniu.

To znaczy: Twoje zapytanie jest interpretowane jako do północy między 30.01.2011 a 31.01.2011, ale dane mogą mieć wartość później tego dnia 31.01.2011.

Sugestia: Zmień pole na typ Data, jeśli jest to typ Data i godzina.


4

Cześć, to zapytanie działa dla mnie,

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

2
select * from person where DATE(dob) between '2011-01-01' and '2011-01-31'

Zaskakujące jest to, że takie konwersje są rozwiązaniem wielu problemów w MySQL.


10
Zaskakujące jest to, że dokładnie to powiedziała zaakceptowana odpowiedź (i kilka innych) ... 2 lata wcześniej.
Chris Baker,

0

Ustaw datę górną na datę + 1 dzień, więc w twoim przypadku ustaw ją na 2011-02-01.


1
Będzie to niepoprawnie obejmowało czas zerowy w dniu 1 lutego… Dlatego BETWEENnależy to zignorować; ale >=i <powinien być używany zamiast tego.
Rozczarowany

0

Możesz uruchomić zapytanie jako:

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

jak inni wskazywali, jeśli twoje daty są zakodowane na stałe.

Z drugiej strony, jeśli data jest w innej tabeli, możesz dodać dzień i odjąć sekundę (jeśli daty są zapisane bez sekundy / godziny), na przykład:

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

Unikaj rzucania na pola dob(tak jak w akceptowanej odpowiedzi), ponieważ może to powodować ogromne problemy z wydajnością (np. Niemożność użycia indeksu w dobterenie, zakładając, że taki istnieje). Plan wykonania może zmienić się z using index conditionna, using wherejeśli zrobisz coś podobnego DATE(dob)lub CAST(dob AS DATE), więc bądź ostrożny!


0

W MySql między wartościami są uwzględniane, dlatego gdy podajesz, spróbuj uzyskać między „2011-01-01” a „2011-01-31”

będzie zawierał od 2011-01-01 00:00:00do góry, 2011-01-31 00:00:00 więc faktycznie w 2011-01-31 nic nie powinno od jego czasu minąć2011-01-31 00:00:00 ~ 2011-01-31 23:59:59

W przypadku górnej granicy możesz zmienić na, 2011-02-01wtedy otrzyma wszystkie dane do góry2011-01-31 23:59:59

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.