Предложение MySQL «между» не включает?


144

Если я запускаю запрос с betweenпредложением, он, кажется, исключает конечное значение.
Например:

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

Это дает все результаты с dob'2011-01-01' до '2011-01-30'; пропуск записей, где dobстоит '2011-01-31'. Может ли кто-нибудь объяснить, почему этот запрос ведет себя таким образом, и как я могу изменить его, чтобы включить записи, где dob'2011-01-31'? (без добавления 1 к дате окончания, потому что она была выбрана пользователями.)


Неа. Моя установка MySQL (версия?) BETWEENВключает оба значения. У меня MySQL Server 5.7Windows 10.
Green

Ответы:


188

Поле, dobвероятно, имеет временную составляющую.

Чтобы обрезать его:

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

61
Вместо этого CAST(dob AS DATE)можно использовать более лаконичный DATE(dob).
jkndrkn

12
Хотя это работает, вы получите лучшую производительность, используя >=и <вместо between.
Дэвид Харкнесс

114
Вы получите лучшую производительность, используя dob BETWEEN '2011-01-01 00:00:00' AND '2011-01-31 23:59:59. Это связано с тем, DATE(dob)что необходимо вычислять значение для каждой строки и нельзя использовать какие-либо индексы в этом поле.
joshuahedlund

2
@joshuahedlund Пожалуйста, добавьте ответ с этим решением. CAST не так эффективен.
doc_id

3
@joshuahedlund Это работает, пока у вас не будет данных со временем t > 23:59:59 and t < 24:00:00. Зачем BETWEENвообще иметь дело с плохо определенным ? Скорее следовать советам и использовать Давида: WHERE dob >= '2011-01-01' AND dob < '2011-02-01'. Лучшая производительность, и она работает каждый раз.
Разочарованный

304

Из руководства по MySQL :

Это эквивалентно выражению (min <= expr AND expr <= max)


3
Руководство, связанное с этим ответом, показывает, что приведение предпочтительнее при сравнении объектов DATE и DATETIME. Так что я думаю, что у @tiagoinu есть наиболее полный ответ в самом строгом смысле, но оба точны.
Kingsolmn 05

@jemminger может быть потому, что ответ от главного соперника - парня из postgres: P
nawfal

27
Короче говоря, между включает ... вот почему этот ответ потрясает.
Рафаэль

6
Старый комментарий, но я хотел привязать его к конкретному запросу. «BETWEEN» является включительно, но даты без указания времени дополняются до 00:00:00. Таким образом, сравнение по диапазону дат приведет к потере последнего дня. Либо позвоните DATE (dob), либо укажите конец дня.
wintermute92,

они говорят, что практика - это золото, исходя из моего варианта использования, она не является всеобъемлющей, мне интересно, почему это происходит со мной. Я пробовал, и иногда это работает, иногда нет. используя его в поле данных TIME.
Джеффри Тхагинтоки

99

Проблема в том, что 31.01.2011 на самом деле это 00:00:00 2011-01-31. Это начало дня. Днем все не входит.


19
Это действительно объясняет, что происходит, и отвечает на вопрос.
Иван П

3
После стольких лет этот ответ по-прежнему остается лучшим. Большое спасибо.
Strabek

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

1
Я думаю, стоит отметить, что это не будет включать даты на, 2011-01-31 23:59:59но будет включать даты до 2011-01-31 23:59:58 последней секунды дня, не включенные. Это может быть незначительно, но кому-то это будет полезно.
doc_id

1
rahmanisback из документации MySQL. Я могу подтвердить, что последняя секунда БУДЕТ включена, поскольку BETWEEN включает оба направления. см. dev.mysql.com/doc/refman/5.5/en/…
Felype

1
Да, @Felype, ты прав. Я сам проверил это в базе данных mysql. Он также включает 23:59:59в себя результат. Так что оба пути включены.
Lucky

2
Если dobстолбец представляет собой метку времени с точностью до секунды, то BETWEENсобытия в течение последней секунды дня по-прежнему не будут пропускаться, если вместо этого не будет использоваться «2011-02-01 00:00:00»?
Азот

1
-1. Не буду включать 2011-01-31 23:59:59.003. Использование @nitrogen 2011-02-01 000:00:00будет неправильно включать нулевое время 1 февраля .... Вот почему >=и <следует использовать его.
Разочарованный

6

Является ли поле, на которое вы ссылаетесь в своем запросе, типом даты или типом DateTime ?

Типичная причина описываемого вами поведения - это использование типа DateTime, тогда как вам действительно следует использовать тип Date. То есть, если вам действительно не нужно знать, в какое время кто-то родился, просто используйте тип Date.

Причина, по которой последний день не включается в ваши результаты, заключается в том, что запрос принимает временную часть дат, которую вы не указали в своем запросе.

То есть: ваш запрос интерпретируется как «до полуночи» между 30 января 2011 года и 31 января 2011 года, но данные могут иметь значение днем ​​позже 31 января 2011 года.

Предложение: измените поле на тип Date, если это тип DateTime.


4

Привет, этот запрос работает для меня,

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'

Удивительно, но такие преобразования - решение многих проблем в MySQL.


10
Удивительно, но это именно то, что было сказано в принятом ответе (и некоторых других) ... за 2 года до вас.
Крис Бейкер

0

Установите верхнюю дату на date + 1 день, поэтому в вашем случае установите ее на 2011-02-01.


1
Это будет неправильно включать нулевое время 1 февраля .... Вот почему его BETWEENследует игнорировать; но >=и <должен быть использован вместо.
Разочарованный

0

Вы можете запустить запрос как:

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

как указывали другие, если ваши даты жестко запрограммированы.

С другой стороны, если дата находится в другой таблице, вы можете добавить день и вычесть секунду (если даты сохранены без секунды / времени), например:

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

Избегайте выполнения приведений к dobполям (как в принятом ответе), потому что это может вызвать огромные проблемы с производительностью (например, невозможность использовать индекс в dobполе, если он есть). План выполнения может измениться с using index conditionна, using whereесли вы сделаете что-то вроде DATE(dob)или CAST(dob AS DATE), поэтому будьте осторожны!


0

В MySql значения включены, поэтому, когда вы даете, попробуйте попасть между '2011-01-01' и '2011-01-31'

он будет включать с 2011-01-01 00:00:00начала до 2011-01-31 00:00:00 31 января 2011 года, так как его время должно идти с2011-01-31 00:00:00 ~ 2011-01-31 23:59:59

Для верхней границы вы можете изменить на, 2011-02-01тогда он получит все данные до2011-01-31 23:59:59

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.