Как выбрать с помощью объединений и вернуть только строки из одной таблицы? - PullRequest
1 голос
/ 04 мая 2019

У меня есть база данных, которая выглядит так: https://i.imgur.com/dwaqUF2.png

Я хочу выбрать все номера, в которых нет бронирования на данный период времени.

Вот что я попробовал:

SELECT idRoom, type, beds FROM Room r
INNER JOIN Reservation_has_Room has on r.idRoom = has.Room_idRoom
INNER JOIN Rezervation re on has.Reservation_has_Room = re.idReservation
 WHERE (re.checkIn<'2019-06-04'
 AND re.checkOut<'2019-06-01') 
 or
(re.checkIn>'2019-06-04'
 AND re.CheckOut>'2019-06-01');

Но этот скрипт возвращает номер каждый раз, когда находит резервирование, которое не совпадает с датой.

редактирование:

Думаю, меня неправильно поняли. В этом случае: В комнатах у меня есть эти записи:

 idRoom, type, beds
'1', 'Standard', '1'
'2', 'Standard', '1'

Бронирование:

 idReservation, checkIn, checkOut
'1', '2019-05-22', '2019-06-03'
'2', '2021-05-22', '2021-06-03'
'3', '2022-05-22', '2022-06-03'

Reservation_has_Room

 Reservation_idReservation, Room_idRoom
'1', '1'
'2', '1'
'3', '1'

Итак, как вы можете видеть для номера № 1, у меня есть 3 бронирования. Один пересекается, а остальные нет. В этом случае скрипт из @AlexYes возвращает это:

 idRoom, type, beds
'1', 'Standard', '1'
'1', 'Standard', '1'
'2', 'Standard', '1'

Так что он возвращает комнату номер один, даже два раза. Мой ожидаемый результат:

 idRoom, type, beds
'2', 'Standard', '1'

Так что доступна только комната № 2.

Ответы [ 4 ]

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

Я бы использовал not exists:

SELECT r.*
FROM Room r
WHERE NOT EXISTS (SELECT 1
                  FROM Reservation_has_Room rhr INNER JOIN 
                       Rezervation re
                       ON rhr.Reservation_has_Room = re.idReservation

                  WHERE r.idRoom = rhr.Room_idRoom AND
                        re.checkIn <= '2019-06-04' AND
                        re.checkOut >= '2019-06-01'
                 );

Логика перекрытий проста. Если кто-то регистрирует одну или до последней даты , а проверяет в первый день или позже, тогда перекрытие происходит, и комната недоступна.

Это учитывает все возможные способы перекрытия интервалов. Обратите внимание, что это включает в себя все четыре дня; Вы можете или не хотите включать первый и последний дни в зависимости от ваших правил регистрации заезда / отъезда.

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

Да, потому что вы выполняете внутреннее соединение и фильтруете резервирования.Что вам нужно, так это то, что номера, оставленные в резервациях, присоединяются к условиям периода времени, а затем отфильтровывают единицы, к которым не присоединились резервации:

SELECT idRoom, type, beds 
FROM Room r
LEFT JOIN ( -- subquery that returns reservation business entities filtered by your time period
    SELECT *
    FROM Reservation_has_Room has
    JOIN Rezervation re
    ON has.Reservation_has_Room = re.idReservation
    WHERE (re.checkIn <= '2019-06-01' AND re.checkOut >= '2019-06-04') -- (1)
    OR re.checkIn between '2019-06-01' and '2019-06-04' -- (2)
    OR re.checkOut between '2019-06-01' and '2019-06-04' -- (3)
) rr
ON r.idRoom = rr.Room_idRoom
WHERE rr.checkIn is null -- filter out units that don't have matching reservations

резервирования сначала фильтруются по 3 случаям:

1) полностью перекрывающиеся резервирования 2) резервирования, которые начинаются где-то в середине периода и заканчиваются через некоторое время после 3) резервирования, которые начинаются некоторое время назад и заканчиваются где-то в середине периода

Условие, предложенноеТим также может работать.Но главное - переключиться на LEFT JOIN с фильтром диапазона в условии соединения и иметь WHERE re.checkIn is null

. Чтобы сделать ваше решение более динамичным, вы должны использовать подзапросы, чтобы указать временные границы только один раз, и использовать их повторно.значения в фильтре:

WITH
 start_date as (select '2019-06-01'::date)
,end_date as (select '2019-06-04'::date)

и имеют это как условие соединения:

(
    re.checkIn <= (select * from start_date) AND re.checkOut >= (select * from end_date) -- (1)
    or re.checkIn between (select * from start_date) and (select * from end_date) -- (2)
    or re.checkOut between (select * from start_date) and (select * from end_date) -- (3)
)
1 голос
/ 04 мая 2019

Я почти уверен, что вам нужны только 2 таблицы.Свойства комнат и свойства бронирования.Если номер находится в таблице бронирования, то он, очевидно, имеет бронирование.Итак:

room_id start_date end_date
 1       s_d1        e_d1 
 1       s_d2        e_d2
 2       s_d3        e_d3

Вы хотите найти номер_идентификатора для диапазона дат клиента, который не забронирован.(обычно до 12 часов)

cust_s_d НЕ МЕЖДУ s_d и e_d или cust_e_d НЕ МЕЖДУ s_d и e_d

select room_id from reservations 
where
cust_s_d NOT BETWEEN s_d and e_d
AND 
cust_e_d NOT BETWEEN s_d and e_d;

объединяют результат со свойствами комнаты, и это все.

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

Это проблема перекрывающегося диапазона, и ваше предложение WHERE должно быть:

WHERE
    re.checkIn <= '2019-06-04' AND
    re.checkOut >= '2019-06-01'

Ваш полный запрос:

SELECT
    idRoom,
    type,
    beds
FROM Room r
INNER JOIN Reservation_has_Room has
    ON r.idRoom = has.Room_idRoom
INNER JOIN Rezervation re
    ON has.Reservation_has_Room = re.idReservation
WHERE
    re.checkIn <= '2019-06-04' AND
    re.checkOut >= '2019-06-01';
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...