如何处理具有排除约束的表的更新?
Posted
技术标签:
【中文标题】如何处理具有排除约束的表的更新?【英文标题】:how to handle updates on a table with exclusion constraint? 【发布时间】:2021-09-28 09:56:18 【问题描述】:我有一个表格来跟踪带有排除约束的酒店预订,如下所示。
目前,我允许客人更新他们的预订。因此,如果 guest_id 1 将他的预订从 3 天预订更改为从 2010-01-03、2010-01-03 开始的一天,如果我运行此更新语句,postgres 将由于重叠约束而阻止更新:
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
那么您如何允许此更新?您是否必须保持预订 id 相同,并删除其他的?
** 注意:我实际上是每行存储一天,因为我还有其他属性可以每天跟踪**
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
【问题讨论】:
不,不会。只要确保你使用UPDATE
,而不是INSERT
。
我明白了。这样做是否可以将guest_id包含在这样的要点中?排除使用 GIST (guest_id with , room WITH =, tsrange(from_ts,to_ts) WITH &&)
不,不要添加客人。
我更新了我的问题,使其更接近于我存储数据的方式。
我用插入语句更新了这个问题,您可以运行这些语句来重现它。 create table 语句应该可以工作。实际上,我仍在探索这一点。我认为这是明确的方式。您是否建议将“during”之类的显式字段与排除约束一起使用?
【参考方案1】:
您可以通过使用不同的排除约束而不是您创建的排除约束来解决该问题:
ALTER TABLE reservation ADD EXCLUDE USING gist (
room WITH =,
guest_id WITH <>,
tsrange(from_ts, to_ts, '[]') WITH &&
);
这将排除两个房间相等的条目并且时间戳范围重叠并且预订是针对不同的客人。
请注意,我使用的是表达式而不是 during
。避免数据库设计中的冗余是一个好主意。当然你也可以保留duration
,去掉from_ts
和to_ts
。
还要注意to_ts
的尴尬程度,在 23:59:00 结束。您可以改为选择在上端开放的区间:
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)
这不会与以 2021-01-03 00:00:00 开头的范围重叠。
【讨论】:
我也尝试将 guest_id 包含在要点中。它可以工作,但是当我尝试将遗留数据迁移到可能包含重叠的表中时,我注意到与没有 guest_id 的 gist 相比,它的运行速度非常慢。你碰巧知道是这样吗? 我不知道,但<>
通常不是很挑剔。您可以尝试将该条件放在索引的最后;我不知道这是否会有很大的不同。
谢谢!它实际上已经在索引的最后一个。我认为它不像你说的那样有选择性。可能坚持将其从要点中排除并在我猜的代码中处理它。
迁移数据不就是一次性的吗?让它慢慢来。在那之后它不应该打扰你。以上是关于如何处理具有排除约束的表的更新?的主要内容,如果未能解决你的问题,请参考以下文章
出现:“数据库中已存在名为 'Assembles' 的对象。”问题,无法更新数据库,请问知道如何处理吗??