Предложение 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 к дате окончания, потому что она была выбрана пользователями.)

ASD
источник
Неа. Моя установка 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'
tiago2014
источник
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.

JohnFx
источник
4

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

select * from person where dob between '2011-01-01' and '2011-01-31 23:59:59'
infinito84
источник
2
select * from person where DATE(dob) between '2011-01-01' and '2011-01-31'

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

betty.88
источник
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

Ambleu
источник