Система бронирования отелей, проблема с поиском свободных номеров - PullRequest
0 голосов
/ 12 мая 2019

это моя схема БД

SELECT r.id FROM room r LEFT JOIN reservation rs ON rs.room_id = r.id
WHERE (rs.date_in < :dateIn AND rs.date_out <= :dateOut)
   OR (rs.date_in >= :dateIn AND rs.date_out > :dateOut)
   OR rs.id IS NULL;

Запрос на показ номеров без бронирования

После бронирования, например, изС 2019-07-12 по 2019-07-14 в комнате № 1, затем при выполнении sql-запроса с dateIn 2019-07-12 и dateOut 2019-07-14, в качестве ответа я получаю комнату № 1, что неверно, посколькуза это время оговорка была сделана.

Ответы [ 4 ]

1 голос
/ 12 мая 2019

Вы хотите исключить конфликтующие бронирования.

Давайте начнем с рассмотрения запрашиваемого периода (A ... B) и гипотетического резервирования (C ... D), мы можем отобразить все возможности и увидеть, что есть несколько возможностей для конфликта:

A.....B  C.....D       OKAY: note B < C never happens otherwise
A........C..B..D       NOT OKAY    (B inside C..D)
A........C.....D..B    NOT OKAY    (C inside A..B, D inside A..B)
         C..A..D..B    NOT OKAY    (A inside C..D, D inside A..B)
         C.A.B.D       NOT OKAY    (A inside C..D, B inside C..D)
         C.....D A..B  OKAY: note A > D never happens otherwise
A...B                  OKAY        (no C...D reservation at all)

Таким образом, в принципе, ни один из элементов этого интервала не должен находиться внутри другого интервала:

NOT (
    (:dateIn BETWEEN rs.date_in AND rs.date_out)
    OR
    (:dateOut BETWEEN rs.date_in AND rs.date_out)
    OR
    (rs.date_in BETWEEN :dateIn AND :dateOut)
    OR
    (rs.date_out BETWEEN :dateIn AND :dateOut)
)

и это должно включать в себя сбой запроса (нет записи rs вообще).

Видя ту же диаграмму, мы также видим, что можем просто запросить B

rs.date_in > :dateOut OR rs.date_out < :dateIn

И так как вам нужны комнаты, для которых нет конфликта, вы должны поместить приведенный выше запрос в предложение JOIN, предназначенное для find конфликтов, с LEFT JOIN и запросом «no конфликта» (rs info равен NULL) в WHERE, чтобы определить, где не выполняется предложение JOIN:

SELECT rooms.* 
    FROM rooms 
    LEFT JOIN reservations ON
    (
        rooms.id = reservations.room_id
        AND (
            NOT (reservations.date_in > :dateOut OR reservations.date_out < :dateIn)
        )
    )
) WHERE rooms.id = :id AND reservations.room_id IS NULL

Предложение JOIN можно упростить, поскольку NOT (A OR B) равно NOT A AND NOT B, а NOT > равно <=:

    (
        rooms.id = reservations.room_id
        AND reservations.date_in  <= :dateOut 
        AND reservations.date_out >= :dateIn
    )

, который немедленно сообщает нам удобный индекс для reservations:

    CREATE INDEX reservations_chk ON reservations(room_id, date_in, date_out, rs_id);

Вы хотите проверить это и проверить, что происходит, когда резервирование заканчивается в один день, и запрос начинается в тот же день (и то же самое с резервированием, начинающимся по окончании запроса). В зависимости от политики отеля вы можете исключить равенство из одного или обоих условий ограничения. Это связано с тем, что дни базы данных «начинаются» и «заканчиваются» в полночь, но реальные резервирования могут начинаться, скажем, в 11:00 и заканчиваться в 10:00, чтобы обеспечить уборку и подготовку комнаты.


Видя, что такой запрос может содержать ключевые слова SELECT, JOIN, бронирование, номер, доступные и т. Д., Вы можете найти в Google и найти также такие документы, как this или this , или это в переполнении стека, что может быть интересно (я только кратко их рассмотрел - caveat emptor ).

0 голосов
/ 12 мая 2019

Я бы использовал NOT EXISTS и следующую логику:

select r.id
from room r
where not exists (select 1
                  from reservation rs 
                  where rs.room_id = r.id and
                        rs.date_in < :dateOut and
                        rs.date_out >= :dateIn
                 );

Логика для перекрытия временных интервалов на самом деле проста - но, возможно, хитра.Два интервала перекрываются, когда выполняется следующее:

  • Первый начинается до окончания второго.
  • Первый заканчивается после запуска второго.

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

Вы можете обозначить это как left join.Я считаю, что NOT EXISTS лучше отражает именно то, что вы пытаетесь найти - комнату без бронирования в течение определенного периода.

0 голосов
/ 12 мая 2019

Вы можете использовать NOT EXISTS и применять условия:

SELECT r.id FROM room r 
WHERE NOT EXISTS (
  SELECT 1 FROM reservation rs
  WHERE rs.room_id = r.id AND (
    (rs.date_in BETWEEN :dateIn AND :dateOut) OR
    (rs.date_out BETWEEN :dateIn AND :dateOut) OR
    (rs.date_in < :dateIn AND rs.date_out > :dateOut)
  )
)
0 голосов
/ 12 мая 2019

Ваше WHERE предложение неверно, попробуйте:

WHERE rs.date_out <= :dateIn OR rs.date_in >= :dateOut OR rs.id IS NULL;

Что означает: текущий гость уйдет до того, как этот прибудет, или следующий гость прибудет после того, как этот уедет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...