MySQL: сопоставление записей, у которых есть x последовательных дат, доступных между двумя датами

Предпосылки / Применение

У меня есть база данных MySQL, содержащая таблицу арендуемой собственности и таблицу бронирования этих свойств. Существует также функция поиска для поиска доступных свойств между двумя указанными датами. При поиске пользователь может ввести дату начала, количество дней, в которых он хочет остаться, и гибкость даты до +/- 7 дней. Бронирование может начаться в тот же день, когда заканчивается другое бронирование (группа 1 уезжает утром, группа 2 прибывает вечером).

Мне сложно эффективно реализовать функцию гибкости.

Схема

CREATE TABLE IF NOT EXISTS `property` (
    `id` bigint(20) NOT NULL AUTO_INCREMENT,
    `name` varchar(60) COLLATE utf8_unicode_ci DEFAULT NULL,
    PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

CREATE TABLE IF NOT EXISTS `property_booking` (
    `id` bigint(20) NOT NULL AUTO_INCREMENT,
    `property_id` bigint(20) DEFAULT NULL,
    `name` varchar(60) COLLATE utf8_unicode_ci DEFAULT NULL,
    `date_start` date DEFAULT NULL,
    `date_end` date DEFAULT NULL,
    PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

Образец данных

INSERT INTO `property` (`name`) 
VALUES ('Property 1'), ('Property 2'), ('Property 3');

INSERT INTO `property_booking` (`property_id`,`name`,`date_start`,`date_end`) 
VALUES (1, 'Steve', '2011-03-01', '2011-03-08'), 
(2, 'Bob', '2011-03-13', '2011-03-20'), 
(3, 'Jim', '2011-03-16', '2011-03-23');

Пример сценария

Пользователь выбирает, что он хочет начать свое пребывание 10 марта 2011 года, он хочет остаться на 7 дней, и у него есть гибкость +/- 2 дня. Я собрал изображение, которое визуализирует данные и параметры ниже. (Красный: Бронирование 1, Зеленый: Бронирование 2, Полосы: Бронирование 3, Синий: Диапазон дат (2011-03-10, + 7 дней и +/- 2 дня гибкости))

Ожидаемый результат

Свойство 1 (Резервирование доступно в любом диапазоне дат)
Свойство 3 (Резервирование доступно начиная с 08.03.2011 или 2011-03-09)

Текущий метод

Мой текущий запрос проверяет на перекрытие для всех 7-дневных диапазонов дат в пределах общего диапазона дат, доступного для поиска, например:

SELECT p.`id`, p.`name` 
FROM `property` p 
WHERE (NOT (EXISTS (SELECT p2.`name` FROM `property_booking` p2 WHERE (p2.`property_id` = p.`id` AND '2011-03-10' < DATE_SUB(p2.`date_end`, INTERVAL 1 DAY) AND '2011-03-17' > DATE_ADD(p2.`date_start`, INTERVAL 1 DAY))))) 
OR (NOT (EXISTS (SELECT p3.`name` FROM `property_booking` p3 WHERE (p3.`property_id` = p.`id` AND '2011-03-11' < DATE_SUB(p3.`date_end`, INTERVAL 1 DAY) AND '2011-03-18' > DATE_ADD(p3.`date_start`, INTERVAL 1 DAY))))) 
OR (NOT (EXISTS (SELECT p4.`name` FROM `property_booking` p4 WHERE (p4.`property_id` = p.`id` AND '2011-03-09' < DATE_SUB(p4.`date_end`, INTERVAL 1 DAY) AND '2011-03-16' > DATE_ADD(p4.`date_start`, INTERVAL 1 DAY))))) 
OR (NOT (EXISTS (SELECT p5.`name` FROM `property_booking` p5 WHERE (p5.`property_id` = p.`id` AND '2011-03-12' < DATE_SUB(p5.`date_end`, INTERVAL 1 DAY) AND '2011-03-19' > DATE_ADD(p5.`date_start`, INTERVAL 1 DAY)))))
OR (NOT (EXISTS (SELECT p6.`name` FROM `property_booking` p6 WHERE (p6.`property_id` = p.`id` AND '2011-03-08' < DATE_SUB(p6.`date_end`, INTERVAL 1 DAY) AND '2011-03-15' > DATE_ADD(p6.`date_start`, INTERVAL 1 DAY)))));

В примере набора данных это достаточно быстро, но на гораздо больших наборах данных он будет довольно медленным, тем более, когда вы создадите полную гибкость +/- 7 дней.

Есть ли у кого-нибудь какие-либо предложения относительно того, как этот запрос можно было бы лучше написать?

11
задан Kris 17 February 2011 в 16:10
поделиться