尝试优化SQL查询

时间:2017-12-21 01:08:51

标签: sql postgresql

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

我有一个列出来宾的guest表。客人可以预订0次或以上

hotel7=# SELECT * FROM guest;
 guest_id |  name   |    phone     |     email     
----------+---------+--------------+---------------
    1 | kat     | 111-111-1111 | kat@g.com
    2 | andy    | 222-222-2222 | andy@g.com
    3 | theda   | 333-333-3333 | theda@g.com
    4 | forrest | 444-444-4444 | forrest@g.com
    5 | trent   | 555-555-5555 | trent@g.com
    6 | cyle    | 666-666-6666 | cyle@g.com
(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)

2 个答案:

答案 0 :(得分: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列。
  4. 此外,问题陈述会要求您为在同一天预订两间客房的客人预订。该声明 NOT 仅限于多个房间预订;如果客人符合此条件的任何预订,则严格阅读该问题会为客人找到所有预订。

    考虑到这一点,并假设您已有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
    

答案 1 :(得分: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