[ Поиск ] - [ Пользователи ] - [ Календарь ]
Полная Версия: Сложная выборка по диапазону дат.
Visine
Для начала опишу ситуацию.
Есть таблица рекламных конструкций следующего вида
----------------
| id | name |
----------------
Где id - уникальный номер конструкции, а name соотвественно ее наименование
Также есть табица в которой указаны периоды когда и какая конструкция занята
Таблица имеет следующий вид
-------------------------------------
| p_id | date_from | date_to |
-------------------------------------
p_id - номер конструкции
date_from - дата начала периода
date_to - дата окончания периода

Требуется по двум заданным датам определить какие конструкции свободны в этот период.
Притом методик поиска должно быть две.
Первая методика должна показывать только те конструкции которые полностью свободны в заданном интервале.
Вторая методика должна показывать конструкции которые хотябы один день (а лучше чтобы можно было задавать минимальное количество дней) свободны в заданном интервале.

Если у кого есть какие идеи или готовые решения поделитесь pls. За мною не заржавеет.



Спустя 4 часа, 19 минут, 58 секунд (26.07.2008 - 05:15) kirik написал(а):
чисто логически -

Первая методика - есть дата начала и дата конца свободного периода конструкции , следовательно для того чтобы вывести конструкции полностью свободные в заданном интервале, нам нужно чтобы дата начала периода была меньше или равна даты начала свободного периода конструкции, а дата конца былабы больше или равна..так?
немного непонятно наверное написал..
Код
$start = 'такая-то дата';
$end = 'такая-то дата';
mysql_query("SELECT `p_id` WHERE `date_from` >= '$start' AND `date_to` <= '$end'");

-это выведет все конструкции свободные на всем промежутке времени...

Вторая методика - нужно чтоб хоть какая-то введенная дата попала в промежуток какой-либо конструкции.
Код
$start = 'такая-то дата';
$end = 'такая-то дата';
mysql_query("SELECT `p_id` WHERE `date_from` >= '$start' OR `date_to` <= '$end'");

-правильно?
а вот с днями...нужно вывести все конструкции попашие под запрос, и потом уже оперировать с датами, сравнивать, вычитать, выводить нужные..
как-то так, я думаю)

Спустя 1 день, 8 часов, 42 минуты, 35 секунд (27.07.2008 - 13:58) Alchemist написал(а):
ИМХО, kirik не прав.

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

На самом деле, запросы такого вида - действительно хитрее чем обычные, потому что приходится делать выборку по условию, которого как бы нет (в том смысле, что нет таких данных). Лично я в таком случае пытаюсь инвертировать условие и делать выборку уже по нему.

Так в данном случае вместо того чтобы искать конструкции полностью свободные в данном промежутке (а таких данных у нас нет), можно найти (и потом отбросить) все конструкции полностью или частично занятые в между двух дат (такие данные у нас есть). Оставшиеся после отбрасывания конструкции как раз и будут полностью свободными.
Код
# $tconst - таблица конструкций
# $tdates - таблица занятости
# $start - начало поискового периода
# $end - конец поискового периода

SELECT * FROM `{$tconst}` LEFT JOIN (
# тут будем искать занятые конструкции
    SELECT DISTINCT `p_id`
    FROM `{$tdates}`
    WHERE `date_from` <= '{$end}' AND `date_to` >= '{$start}'
) AS `tmp` ON (`{$tconst}`.`id` = `{$tdates}`.`p_id`)
WHERE 1
HAVING `tmp`.`p_id` IS NULL   # отбрасываем найденное

Вот примерно как-то так...


Во втором случае нам подходят не только полностью свободные, но и частично занятые конструкции. Следовательно отбрасывать надо только те, которые полностью заняты в указаный период. Однако, изменение условия в подзапросе на:
Код
    WHERE `date_from` <= '{$start}' AND `date_to` >= '{$end}'

не охватывает все возможные комбинации. Так например :
10 | 2008-01-01 | 2008-01-15
10 | 2008-01-16 | 2008-01-31

ни одна из этих строк не отвечает приведенному условию, тем не менее конструкция 10 занята весь январь.

Опять же, есть разные способы решения. Мой вариант - считать дни:
Код
SELECT * FROM `{$tconst}` LEFT JOIN (
SELECT `p_id`, SUM(MIN(TO_DAYS(`date_to`),TO_DAYS('{$end}')) - MAX(TO_DAYS(`date_from`),TO_DAYS('{$start}')) + 1) as `days`
FROM `{$tdates}`
WHERE `date_from` <= '{$end}' AND `date_to` >= '{$start}'
GROUP BY `p_id`
) AS `tmp` ON (`{$tconst}`.`id` = `{$tdates}`.`p_id`)
WHERE 1
HAVING `tmp`.`days` IS NULL
OR `tmp`.`days` < TO_DAYS('{$end}') - TO_DAYS('{$start}') + 1

Выделенный кусок запроса отвечает за пограничное кол-во занятых дней (в данном случае - весь период), т.е. уменьшая это число можно регулировать минимальное кол-во свободных дней.

ЗЫ: Предполагается что все граничные даты используются включительно.
Быстрый ответ:

 Графические смайлики |  Показывать подпись
Здесь расположена полная версия этой страницы.
Invision Power Board © 2001-2024 Invision Power Services, Inc.