Как составить правильный запрос к MySQL с условием по дате?

Главные вкладки

Аватар пользователя ingumsky@drupal.org ingumsky@drupal.org 11 января 2009 в 2:42

Добрый вечер!

Пишу модуль, который будет отображать блок со ссылками на две ноды (матчи), которые надо выбирать, отталкиваясь от сегодняшней даты. Первая должна быть самой «свежей» из прошедших, вторая — ближайшей из грядущих. Пример: Есть матчи 3 января, 7 января, 10 января, 13 января и 15 января. Мне нужно, чтобы запрос 11 января выдёргивал из базы только матчи 10 и 13 января и т.д, 12 января — тоже самое, а 14 января уже матчи 13 и 15 января и т.д.

Вопрос первый: как сделать это одним запросом к базе? Решил пойти через ORDER BY ABS(DATEDIFF(gamedatetime,CURDATE())) (модуль разницы между датой матча и текущим днём) LIMIT 2, но не уверен, что это наилучший вариант, хотя ответы он мне выдаёт правильные.
Вопрос второй: будет ли подобный запрос кошерным (да ещё и с использованием функций MySQL по обработке дат и математическими операциями) или лучше всё-таки разделить на два?
Вопрос третий: как бы вы посоветовали правильно кэшировать блок с таким запросом, результат которого может оставаться неизменным в течение нескольких дней (так как перерывы между играми бывают большими)?

В принципе, можно добавить к моему запросу что-нибудь типа WHERE gamedatetime (поле даты матча) BETWEEN DATE_SUB(CURDATE(), INTERVAL 10 DAY) AND DATE_ADD(CURDATE(), INTERVAL 10 DAY) для того, чтобы сузить поле деятельности запроса, но, как я понимаю, это даст только лишнюю нагрузку на базу.

Заранее спасибо за ответы.

Комментарии

Аватар пользователя penexe penexe 11 января 2009 в 8:11

ИМХО конечно, но мне кажется два запроса с с лимитом, отработают быстрее чем один со всяческими вычислениями

Аватар пользователя ingumsky@drupal.org ingumsky@drupal.org 11 января 2009 в 12:33

Ага. Вот мне тоже почему-то так кажется. Но пока мои два запроса получаются тоже с вычислениями — WHERE gamedatetime < CURDATE() ORDER BY DATEDIFF(gamedatetime,CURDATE()) DESC для события в прошлом и WHERE gamedatetime > CURDATE() ORDER BY DATEDIFF(gamedatetime,CURDATE()) для события в будущем. И что-то у меня туго соображает голова — не могу сообразить, как оптимизировать эти запросы.

Кстати, я уже нашёл потенциальную возможность, чтобы мой запрос через ABS(DATEDIFF(gamedatetime,CURDATE())) отработал не так, как мне надо — если даты событий 3 января, 10 января, 13 января (а такое вполне часто бывает, что между матчами сначала перерыв в неделю, а потом всего пару дней), то 9 января запрос выдаст мне 10 января и 13 января, как наименьшие по модулю.

Аватар пользователя olk olk 11 января 2009 в 13:25

Можно попробовать без лимитов и ордер бай
пример для ноды

select n1.nid,n1.created from {node} n1 inner join
 ( select min(created) dt from {node} where created>1220446629 ) s1 ON n1.created=s1.dt
union all
select n2.nid,n2.created from {node} n2 inner join
 ( select max(created) dt from {node} where created<1220446629 ) s2 ON n2.created=s2.dt

только надо учитывать если есть «точно» совпадающие даты то вернется больше 2-х записей,
тут вместо числа [1220446629] будет конечно текущая дата ну а вместо created ваша дата в базе, ну и таблицы соответственно ваши Smile

Аватар пользователя olk olk 11 января 2009 в 15:00

Ну точно сказать не могу (не знаю как там оптимизатор в MySQL устроен), но насколько я знаю базы данных, ORDER BY почти всегда ведет к фулскану, и только потом к выборке будет применен лимит ...
агрегатные же функции (если по агрегируемому полю есть индекс) скорее всего возьмут адрес записи из индекса,
ну а вообще я не теоретик, если я не прав гуру меня поправят Smile

Аватар пользователя ingumsky@drupal.org ingumsky@drupal.org 12 января 2009 в 17:27

Надо будет попробовать через EXPLAIN прогнать оба запроса, может быть он даст хотя бы приблизительный ответ на то, что использовать лучше, если здесь не найдётся гуру MySQL.