如何选择联接并仅返回一个表中的行?

时间:2019-05-04 09:33:00

标签: mysql sql

我有一个看起来像这样的数据库: 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'

因此,如您所见,房间nr 1我有3个预订。一个是重叠的,其余不是。在这种情况下,@ AlexYes中的脚本返回以下内容:

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

因此它返回一号房间,甚至两次。 我的预期结果是:

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

所以只有2号房可用。

4 个答案:

答案 0 :(得分:1)

这是重叠范围问题,您的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';

答案 1 :(得分:1)

是的,这样做是因为您正在执行内部联接并过滤保留。您需要的是按时间段条件保留剩余房间的房间,然后过滤没有加入任何房间的单元:

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)
)

答案 2 :(得分:1)

我很确定您这里只需要2张桌子。房间属性和预订属性。如果房间在预订表中,则显然有预订。所以:

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

您要查找尚未预订的客户日期范围内的room_id。 (通常到下午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;

将结果与房间的属性结合在一起,就是这样。

答案 3 :(得分:1)

我会使用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'
                 );

重叠的逻辑很简单,如果某人在最后一个日期之前或之后入住,而在第一天或第一天之后退房,则存在重叠并且房间不可用。 / p>

这说明了间隔可能重叠的所有可能方式。请注意,这包括全部四天;您可能会或可能不希望包括第一天和最后几天,具体取决于您的入住/退房规则。