I have a table keep track of hotel reservations with exclusion constraint like below.
Currently, I allow guests to update their reservations. So if guest_id 1 change his reservation from 3 day reservation to a single day from 2010-01-03, 2010-01-03, postgres will block the update due to the overlap constraint if i ran the this update statement:
update reservation set from_ts = '2021-01-03 00:00:00', to_ts='2021-01-10 23:59:00', during = '[2021-01-03 00:00:00, 2021-01-10 23:59:00]' where id = 1
How do you go about allowing this update then? Do you have to keep the reservation id the same, and delete the others?
** note: I'm actually storing a day per row as I have other attributes to keep track at per day basis **
Table: reservation
id | room | from_ts | to_ts | guest_id
---- ------ --------------------- --------------------- ------------
1 | 101 | 2010-01-01 00:00:00 | 2010-01-01 23:59:00 | 1
2 | 101 | 2010-01-02 00:00:00 | 2010-01-02 23:59:00 | 1
3 | 101 | 2010-01-03 00:00:00 | 2010-01-03 23:59:00 | 1
CREATE TABLE reservation (
id int,
guest_id int,
room int,
from_ts timestamp without time zone,
to_ts timestamp without time zone,
during tsrange,
EXCLUDE USING GIST (room WITH =, during WITH &&)
);
-- bootstrap to test the problem
INSERT INTO reservation ( id, guest_id, room, from_ts, to_ts, during ) VALUES ( 1, 1, 101, '2021-01-01 00:00:00', '2021-01-01 23:59:00', '[2021-01-01 00:00:00, 2021-01-01 23:59:00]');
INSERT INTO reservation ( id, guest_id, room, from_ts, to_ts, during ) VALUES ( 2, 1, 101, '2021-01-02 00:00:00', '2021-01-02 23:59:00', '[2021-01-02 00:00:00, 2021-01-02 23:59:00]' );
INSERT INTO reservation ( id, guest_id, room, from_ts, to_ts, during ) VALUES ( 3, 1, 101, '2021-01-03 00:00:00', '2021-01-03 23:59:00', '[2021-01-03 00:00:00, 2021-01-03 23:59:00]' );
-- update statement will fail after you run the insert statements
update reservation set from_ts = '2021-01-03 00:00:00', to_ts='2021-01-10 23:59:00', during = '[2021-01-03 00:00:00, 2021-01-10 23:59:00]' where id = 1
CodePudding user response:
You can solve that problem by using a different exclusion constraint instead of the one you created:
ALTER TABLE reservation ADD EXCLUDE USING gist (
room WITH =,
guest_id WITH <>,
tsrange(from_ts, to_ts, '[]') WITH &&
);
That will exclude two entries where the room is equal and the timstamp ranges overlap and the reservation is for different guests.
Note that I used an expression rather than during
. It is a good idea to avoid redundancy in database design. You can of course also keep the duration
and do away with from_ts
and to_ts
.
Also note how awkward to_ts
is, ending at 23:59:00. You may instead choose intervals that are open at the upper end:
SELECT tsrange('2021-01-02 00:00:00', '2021-01-03 00:00:00');
tsrange
═══════════════════════════════════════════════
["2021-01-02 00:00:00","2021-01-03 00:00:00")
(1 row)
This won't overlap with a range that starts with 2021-01-03 00:00:00.