因此,我们正在寻找一种方法来实施跨越多个表的约束。
我们遇到了this old blog post,这表明:
创建实例化视图以选择违反所需约束的数据。 MV必须用
REFRESH COMPLETE ON COMMIT
定义,以便在事务结束之前进行更新。在物化视图上创建检查约束,该约束总是评估为FALSE –例如
CHECK (1=0)
就是这样。每当更新基础表时,都会刷新实例化视图。如果更新违反了规则,那么将在实例化视图中插入一行;但是对MV的检查约束不允许对其进行任何插入,因此事务失败。
尽管有一些性能问号,但这个想法听起来很合理。
但是,据我们所知,postgresql不支持REFRESH ON COMMIT
之类的东西。
当然,我们可以做的是在构成视图的表上安装触发器,这些触发器将触发对更新/删除/插入的刷新。
但是不仅可能意味着必须对每个涉及的表执行刷新,而且在执行刷新时,我们可能还早已结束了事务。
也许我们可以用锁定来做一些事情,但是这很快就变成了锁定正确的事情的问题,这听起来像一个可怕的主意。
所以我们有什么可以做的,还是我们最好忘掉这个?
我们最接近“提交前刷新”行为的是什么?
不要创建实例化视图,请手动滚动。
作为示例,我们有两个表a
和b
,并且我们要确保这两个表中的行之和小于1000:
BEGIN;
CREATE TABLE counter (
total bigint NOT NULL,
CHECK (total < 1000)
);
CREATE FUNCTION count_trig() RETURNS trigger
LANGUAGE plpgsql AS
$$BEGIN
CASE
WHEN TG_OP = 'INSERT' THEN
UPDATE counter SET total = total + 1;
RETURN NEW;
WHEN TG_OP = 'DELETE' THEN
UPDATE counter SET total = total - 1;
RETURN OLD;
END CASE;
END;$$;
CREATE TRIGGER count_trig AFTER INSERT OR DELETE ON a
FOR EACH ROW EXECUTE PROCEDURE count_trig();
CREATE TRIGGER count_trig AFTER INSERT OR DELETE ON b
FOR EACH ROW EXECUTE PROCEDURE count_trig();
INSERT INTO counter (total)
VALUES ((SELECT count(*) FROM a) + (SELECT count(*) FROM b));
COMMIT;