我有一种情况,我需要确保在任何时候只有一个具有相同 object_id 和 user_id 的活动记录。这是一个有代表性的表格:
CREATE TABLE actions (
id SERIAL PRIMARY KEY,
object_id integer,
user_id integer,
active boolean default true,
created_at timestamptz default now()
);
一次只有一个活动记录,我的意思是你可以有一系列插入,如下所示:
insert into actions (object_id, user_id, active) values (1, 1, true);
insert into actions (object_id, user_id, active) values (1, 1, false);
但做一个后续
insert into actions (object_id, user_id, active) values (1, 1, true);
应该会失败,因为此时已经存在 1 个 object_id = 1 且 user_id = 1 的活动元组。
我正在使用 PostgreSQL 8.4。
我看到这篇文章看起来很有趣,但它是特定于 Oracle 的。
我也看到了这篇文章,但它需要更多地关注事务隔离级别。我认为它不会在读取提交模式下按原样工作。
我的问题是还有哪些其他选项可用于不确定这种约束?
编辑:删除了第一组中的第三个插入。我认为这使示例感到困惑。我还添加了 created_at 时间戳来帮助处理上下文。重申一下,可以有多个 (object_id, user_id, false) 元组,但只有一个 (object_id, user_id, true) 元组。
更新:我接受了克雷格的回答,但对于其他可能偶然发现类似问题的人来说,这是另一种可能的(虽然不是最佳的)解决方案。
CREATE TABLE action_consistency (
object_id integer,
user_id integer,
count integer default 0,
primary key (object_id, user_id),
check (count >= 0 AND count <= 1)
);
CREATE OR REPLACE FUNCTION keep_action_consistency()
RETURNS TRIGGER AS
$BODY$
BEGIN
IF NEW.active THEN
UPDATE action_consistency
SET count = count + 1
WHERE object_id = NEW.object_id AND
user_id = NEW.user_id;
INSERT INTO action_consistency (object_id, user_id, count)
SELECT NEW.object_id, NEW.user_id, 1
WHERE NOT EXISTS (SELECT 1
FROM action_consistency
WHERE object_id = NEW.object_id AND
user_id = NEW.user_id);
ELSE
-- assuming insert will be active for simplicity
UPDATE action_consistency
SET count = count - 1
WHERE object_id = NEW.object_id AND
user_id = NEW.user_id;
END IF;
RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql;
CREATE TRIGGER ensure_action_consistency AFTER INSERT OR UPDATE ON actions
FOR EACH ROW EXECUTE PROCEDURE keep_action_consistency();
它需要使用跟踪表。由于我希望是显而易见的原因,这根本不是可取的。这意味着您在操作中每个不同的(object_id,user_id)都有一个额外的行。
我接受@Craig Ringer 回答的另一个原因是,当给定的操作元组更改状态时,其他表中也存在对 actions.id 的外键引用。这就是为什么历史表在这种情况下不太理想的原因。感谢您的评论和回答。