Некоторые аспекты BETWEEN

ForJest

- свежая кровь
Некоторые аспекты BETWEEN

Всем желающим предлагается развлекательно/поучительный топик по использованию BETWEEN.

1. Часть инициализационная
PHP:
CREATE TABLE min_date (
  id int(11) NOT NULL auto_increment,
  dt_field datetime NOT NULL default '0000-00-00 00:00:00',
  PRIMARY KEY  (id),
  KEY dt_field (dt_field)
) TYPE=MyISAM;

INSERT INTO min_date (id, dt_field) VALUES (1, '2003-02-01 00:00:01');

CREATE TABLE tbl_data (
  id_foreign int(11) NOT NULL default '0',
  d_occurs date NOT NULL default '0000-00-00',
  KEY id_foreign (id_foreign,d_occurs)
) TYPE=MyISAM;

INSERT INTO tbl_data (id_foreign, d_occurs) VALUES 
(1, '2003-02-15'),
(1, '2003-02-16'),
(1, '2003-02-18');
2. Часть - развлекательная
PHP:
SELECT * 
FROM min_date m, tbl_data t 
WHERE m.id=t.id_foreign AND t.d_occurs BETWEEN m.dt_field AND '2004-01-01 23:59:59';
//результат отсутствует, хотя по идее должен был бы

SELECT * 
FROM min_date m, tbl_data t
WHERE m.id = t.id_foreign AND t.d_occurs
BETWEEN '2003-02-01 00:00:01' AND '2004-01-01 23:59:59';
//тоже самое что и первая выборка, по сути, но не исполнению

SELECT * 
FROM min_date m, tbl_data t
WHERE m.id = t.id_foreign AND t.d_occurs >= m.dt_field AND t.d_occurs <= '2004-01-01 23:59:59';
//еще раз тоже самое, другими словами
3. Часть - познавательная.
Ну думаю пытливые умы сами докапаются до истины :)
 

.des.

Поставил пиво кому надо ;-)
я догадался :) а ForJest специально много лишнего написал :) чтобы сложнее было :)
 

ForJest

- свежая кровь
не чтоб сложнее а чтоб интереснее :)
А вообще это кусок реальной задачи... Был... ;)
 

Crazy

Developer
Re: Некоторые аспекты BETWEEN

Автор оригинала: ForJest
t.d_occurs BETWEEN m.dt_field AND '2004-01-01 23:59:59'
А что, мануал по MySQL обещал перевычислять границы для каждой строки выборки? :)
 

.des.

Поставил пиво кому надо ;-)
А что, мануал по MySQL обещал перевычислять границы для каждой строки выборки?
Еще раз ? что значит перевычислять?
тут вся проблема в некорректном преобразовании DATETIME в DATE просто странно что в одном случае он конвертит а в другом нет.

конечно же такие дела вообще некорректны с этим никто не спорит :)
 

Апельсин

Оранжевое создание
там у них была бага с преобразованием DATETIME, DATA, TIMESTAMP, возможно это отголоски той самой.
А вообще подобное некорректное поведение описывается в официальном списке рассылки, а не на форуме.
 

Crazy

Developer
Автор оригинала: .des.
Еще раз ? что значит перевычислять?
Оптимизатор у MySQL странноватый. Он вполне мог зафиксировать значение параметра between для первого попавшегося значения m.dt_field.
 

ForJest

- свежая кровь
Апельсин - сорри я просто даже не подумал что это может быть бага... Я думал это фича - поэтому и решил что это просто аспекты и предложил развлечься с поиском почему :)

expr BETWEEN min AND max
-- If expr is greater than or equal to min and expr is less than or equal to max, BETWEEN returns 1, otherwise it returns 0. This is equivalent to the expression (min <= expr AND expr <= max) if all the arguments are of the same type. The first argument (expr) determines how the comparison is performed as follows:
-- If expr is a TIMESTAMP, DATE, or DATETIME column, MIN() and MAX() are formatted to the same format if they are constants.
-- If expr is a case-insensitive string expression, a case-insensitive string comparison is done.
-- If expr is a case-sensitive string expression, a case-sensitive string comparison is done.
-- If expr is an integer expression, an integer comparison is done.
-- Otherwise, a floating-point (real) comparison is done.

Итак получается:
1. Операнды не одного типа
2. Операнды MAX и MIN не константы.
У меня нет никаких других предположений кроме:
DATE и DATETIME в таком случае рассматриваются в INTEGER контексте.
Т.е. имеем для что-то типа
20030216 BETWEEN 20030201000001 AND 20040101235959
Соответственно даное условие будет ложным.

Оптимизатор у MySQL странноватый. Он вполне мог зафиксировать значение параметра between для первого попавшегося значения m.dt_field.
Но ведь в тестовом примере всего 1 запись в этой таблице - это же ничего не меняет, получается.
 

ForJest

- свежая кровь
Чтобы логично закрыть заявленую тему трэда.
Аспект состоит в том что
expr BETWEEN min AND max
это не
(min <= expr AND expr <= max).
Так как я с этим столкнулся случайно, думаю даже опытному разработчику стоит об это узнать и помнить при составлении запросов. А пример просто показывает к чему это может привести.
А посты наглядно показывают чем это может считаться в сознании программера :).
Никого не хотел обидеть/дискредитировать, если что - сорри.
 

Romantik

TeaM PHPClub
Кстати у меня то же неточности с использованием BETWEEN, которые пропали используя >= AND <=
 

Yurik

/dev/null
точно помню что в changelog 4.0.5 был исправлен глюк с BETWEEN (после обновления у меня пропал глюк)

D.3.8 Changes in release 4.0.5 (13 Nov 2002)
BETWEEN behaviour changed (see section 6.3.1.2 Comparison Operators). Now datetime_col BETWEEN timestamp AND timestamp should work as expected.
P.S. .. чтобы логично закрыть тему треда...
 

Sad Spirit

мизантроп (Старожил PHPClub)
Команда форума
Риторический вопрос: вот не понимаю, какой работы с датами/временем вы ждали от СУБД, разработчики которой считают, что в году 13 месяцев, а в каждом из месяцев по 32 дня? :D
 

.des.

Поставил пиво кому надо ;-)
Sad Spirit :D ну как же без "тонкого" намека на Postgre :D
 

Sad Spirit

мизантроп (Старожил PHPClub)
Команда форума
Автор оригинала: .des.
Sad Spirit :D ну как же без "тонкого" намека на Postgre :D
Это тонкий намёк несколько на другое... Какой смысл говорить о мелких багах работы с датами, если в поле типа "дата" можно засунуть значение, датой a priori не являющееся? ;)

Что больше: '25 часов 05 минут 30 февраля 2003 года' или '1 час 00 минут 31 февраля 2003 года'?
 

Апельсин

Оранжевое создание
> Какой смысл говорить о мелких багах работы с датами, если в поле типа "дата" можно засунуть значение, датой a priori не являющееся?

потому как первое - больше смахивает на багу, а второе - документированная фича ;)
 

su1d

Старожил PHPClubа
странно, у меня либа на Nested Sets полностью построена на BETWEEN, в котором нередко появляются и поля из "той же записи", плюс к тому же ещё разные IF()'ы и т.п.
ни разу не замечал глюков, всё работает как часы.

может не стоит так сразу категорично отзываться о BETWEEN, призывая от него отказываться?

может надо было таки дату приводить к числу перед численным сравнением - 0+'2004-01-01 23:59:59' - так, как это указано в мане?
 

ForJest

- свежая кровь
D.3.8 Changes in release 4.0.5 (13 Nov 2002)
BETWEEN behaviour changed (see section 6.3.1.2 Comparison Operators). Now datetime_col BETWEEN timestamp AND timestamp should work as expected.

P.S. .. чтобы логично закрыть тему треда...
Этот "глюк" отношения не имеет к тестовому примеру.

Кстати у меня то же неточности с использованием BETWEEN, которые пропали используя >= AND <=
может не стоит так сразу категорично отзываться о BETWEEN, призывая от него отказываться?
Я не призывал отказываться от BETWEEN :) Я призывал прочитать ман и помнить, что BETWEEN работает таже как >= <= только в заявленных документацией случаях. В первых же строчках об этом прямо сказано. Это и есть аспекты BETWEEN. Никто не говорит про багу или глюки.

BETWEEN это не >= <=. Я для себя это обнаружил и счастлив поделиться с остальными. К датам этот трэд имеет только косвенное отношение :)

P.S. Хотя не мне вас всех учить ;). Всем спасибо за внимание к.
 

ForJest

- свежая кровь
вы пробовали свой тест на версии >4.0.5 ?
Нет - не тестировал. Может я излишне категорично высказался. Но в тестовом примере идет разница на том что
expr - выражение типа DATE
min - выражение типа DATETIME
max - строковая константа, содержащая литерал DATETIME.
 
Сверху