我正在使用一个postgres数据库,我的问题包括两个表,下面是它们的简化版本。
CREATE TABLE events(
id SERIAL PRIMARY KEY NOT NULL,
max_persons INTEGER NOT NULL
);
和
CREATE TABLE requests(
id SERIAL PRIMARY KEY NOT NULL,
confirmed BOOLEAN NOT NULL,
creation_time TIMESTAMP DEFAULT NOW(),
event_id INTEGER NOT NULL /*foreign key*/
);
有n
个事件,每个事件最多可包含events.max_persons
个参与者。新请求需要确认,有效期最长为30分钟。在此期间之后,如果未确认,请求将被忽略。
现在我想要做的事情只会插入新的request
,此时所有已确认的请求和所有仍然有效但未确认的请求的总和小于{ {1}}。
我已经有了一个选择单个事件的查询。这是它的简化版本,只是为了给你一个想法,它应该如何工作
events.max_persons
是否可以使用单个INSERT命令实现此目的?
答案 0 :(得分:2)
你可以这样做:
INSERT INTO requests
SELECT * FROM (VALUES (...)) row
WHERE ...
并编写一个WHERE
子句,只有在满足条件的情况下才会生效。
但这种方法存在一个根本问题,即它会受到竞争条件的影响。
如果两个这样的语句同时运行,则两者都可以找到条件满足,但是当每个语句都添加了它的行并提交时,可以违反条件。这是因为在提交之前,没有一个语句可以看到另一个语句的效果。
有两种解决方案:
在测试和插入之前锁定表格。这很简单,但对并发性非常不利。
始终使用SERIALIZABLE
个交易。然后,这应该导致序列化错误,并且必须重试其中一个语句,并且会在发生时违反条件。