尝试优化SQL查询

问题描述 投票:1回答:2

我有一个任务,我正在使用一个虚构的酒店数据库。我正在尝试写一个查询“查找为同一日期预订了两个房间的客人的预订”。

我有一张guest表,列出了客人。客人可以预订0次或以上

hotel7=# SELECT * FROM guest;
 guest_id |  name   |    phone     |     email     
----------+---------+--------------+---------------
    1 | kat     | 111-111-1111 | [email protected]
    2 | andy    | 222-222-2222 | [email protected]
    3 | theda   | 333-333-3333 | [email protected]
    4 | forrest | 444-444-4444 | [email protected]
    5 | trent   | 555-555-5555 | [email protected]
    6 | cyle    | 666-666-6666 | [email protected]
(6 rows)

我有一个列出所有预订的reservation表。每个预订有一位客人,可以与一个或多个房间相关联。

hotel7=# SELECT * FROM reservation;
 res_id | guest_id |  check_in  | check_out  
--------+----------+------------+------------
  1 |        1 | 2017-12-01 | 2017-12-03
  2 |        1 | 2017-12-05 | 2017-12-07
  3 |        2 | 2017-12-01 | 2017-12-02
  4 |        2 | 2017-12-01 | 2017-12-10
  5 |        3 | 2017-12-01 | 2017-12-10
  6 |        4 | 2017-12-15 | 2017-12-30
  7 |        5 | 2017-12-15 | 2017-12-22
(7 rows)

room_res表,显示哪些房间属于什么预订

hotel7=# SELECT * FROM room_res;
 res_id | room_id 
--------+---------
  1 |       1
  1 |       2
  2 |       1
  3 |       3
  3 |       4
  4 |       5
  5 |       6
  6 |       1
  7 |       3
(9 rows)

这个命令让我很接近,但我只想向预订2个或更多房间的人展示。

hotel7=# SELECT g.name, r.res_id, r.check_in, r.check_out, rr.room_id
FROM guest AS g
INNER JOIN reservation AS r
ON r.guest_id = g.guest_id
INNER JOIN room_res AS rr
ON rr.res_id = r.res_id;
  name   | res_id |  check_in  | check_out  | room_id 
---------+--------+------------+------------+---------
 kat     |      1 | 2017-12-01 | 2017-12-03 |       1
 kat     |      1 | 2017-12-01 | 2017-12-03 |       2
 kat     |      2 | 2017-12-05 | 2017-12-07 |       1
 andy    |      3 | 2017-12-01 | 2017-12-02 |       3
 andy    |      3 | 2017-12-01 | 2017-12-02 |       4
 andy    |      4 | 2017-12-01 | 2017-12-10 |       5
 theda   |      5 | 2017-12-01 | 2017-12-10 |       6
 forrest |      6 | 2017-12-15 | 2017-12-30 |       1
 trent   |      7 | 2017-12-15 | 2017-12-22 |       3
(9 rows)

所以我尝试了这个并且空了

hotel7=#  SELECT g.name, r.res_id, r.check_in, r.check_out, rr.room_id
FROM guest AS g
INNER JOIN reservation AS r
ON r.guest_id = g.guest_id
INNER JOIN room_res AS rr
ON rr.res_id = r.res_id GROUP BY g.name, r.res_id, r.check_in, r.check_out, 
rr.room_id
HAVING COUNT (rr.room_id)>1;
 name | res_id | check_in | check_out | room_id 
------+--------+----------+-----------+---------
(0 rows)
sql postgresql
2个回答
1
投票

只需从rr.room_idGROUP BY列表中删除SELECT

SELECT g.name, r.res_id, r.check_in, r.check_out
FROM guest AS g
INNER JOIN reservation AS r ON r.guest_id = g.guest_id
INNER JOIN room_res AS rr ON rr.res_id = r.res_id 
GROUP BY g.name, r.res_id, r.check_in, r.check_out
HAVING COUNT (rr.room_id)>1;

当您按房间分组时,您已将每个组的潜在大小限制为一个房间,并且您将永远无法拥有COUNT(room_id)> 1。

但是,这是问题的简单版本。它只适用于您可以准确地假设预订日期排队,并且如果您松散地阅读问题陈述。如果您需要允许check_in / check_out日期不多的预订,事情会变得复杂得多。这是我如何处理:

  1. 生成一个数字表(如果您不知道如何,请使用Google。)
  2. Reservation表加入生成的数字表,其中数值小于DATEDIFF()check_in之间的check_out
  3. 使用此日期结果而不是GROUP BY表达式中的check_incheck_out列。

此外,问题陈述要求您为在同一天预订两间客房的客人预订。声明并不仅限于您预订多个房间;如果客人有任何符合此标准的预订,严格阅读问题将为客人找到所有预订。

考虑到这一点,并假设您已经有一个Numbers表,完整的查询将如下所示:

WITH EligibleGuests As (   
    SELECT r.guest_id
    FROM reservation r
    INNER JOIN Numbers n ON n.Number < DATEDIFF(d, r.check_in, r.check_out) 
    INNER JOIN room_res AS rr ON rr.res_id = r.res_id 
    GROUP BY r.guest_id, DATEADD(d, r.check_in, n.Number)
    HAVING COUNT (rr.room_id)>1
)
SELECT g.name, r.check_in, r.check_out, rr.room_id
FROM Guests g
INNER JOIN EligibleGuests eg on eg.guest_id = g.guest_id
INNER JOIN Reservations r ON r.guest_id = g.guest_ID
INNER JOIN Room_Res rr on rr.res_id = r.res_id

0
投票

我认为这可归结为伪装的重叠范围问题。我们可以尝试自己加入reservation表来检查重叠的预订范围,从而产生两个或更多预留房间。

WITH cte AS (
    SELECT DISTINCT r1.guest_id
    FROM reservation r1
    INNER JOIN room_res rr1
        ON r1.res_id = rr1.res_id
    INNER JOIN reservation r2
        ON r1.check_in < r2.check_out AND
           r1.check_out > r2.check_in AND
           r1.guest_id = r2.guest_id  AND
           r1.res_id < r2.res_id
    INNER JOIN room_res rr2
        ON r2.res_id = rr2.res_id
    WHERE
        rr1.room_id <> rr2.room_id
)

SELECT g.*
FROM guest g
INNER JOIN cte t
    ON g.guest_id = t.guest_id;

enter image description here

Demo

© www.soinside.com 2019 - 2024. All rights reserved.